There was an issue that `KotlinType.equals` called in `KotlinTypeFactory.flexibleType`
and `RawType` constructor produced endless recursion of types that wasn't
computed yet
Effectively, this commit allows for common module
to see internal content of all expect-modules
The problem is that when computing the member scope for A (see the test)
we're building a special member scope for CommonAbstract viewed from JVM
and it's effectively has a fake override of actual member from Jvm/ExpectBase.
OverridingUtil checks if it's visible in CommonAbstract and finds that it's not
thus creating a fake_invisible fake override
The latter results in A::foo override being marked as INVISIBLE_MEMBER_OVERRIDE
Probably, the fix might be smarter
(passing a requested module to OverridingUtil::createAndBindFakeOverride)
but allowing using internal member seems to be safe & simple
because it's reasonable to assume there's no cyclic dependencies
between expected/actual modules
org.jetbrains.kotlin.descriptors.InvalidModuleException: Accessing invalid module descriptor <production sources for module light_idea_test_case> is a module[ModuleDescriptorImpl@89f9a78]
at org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl.assertValid(ModuleDescriptorImpl.kt:51)
at org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl.getPackage(ModuleDescriptorImpl.kt:70)
at org.jetbrains.kotlin.descriptors.FindClassInModuleKt.findClassifierAcrossModuleDependencies(findClassInModule.kt:23)
at org.jetbrains.kotlin.types.KotlinTypeKt.refineDescriptor(KotlinType.kt:185)
at org.jetbrains.kotlin.descriptors.impl.AbstractClassDescriptor$1$1.invoke(AbstractClassDescriptor.java:50)
at org.jetbrains.kotlin.descriptors.impl.AbstractClassDescriptor$1$1.invoke(AbstractClassDescriptor.java:47)
at org.jetbrains.kotlin.types.SimpleTypeImpl.refine(KotlinTypeFactory.kt:210)
at org.jetbrains.kotlin.types.SimpleTypeImpl.refine(KotlinTypeFactory.kt:182)
at org.jetbrains.kotlin.types.AbstractTypeConstructor$ModuleViewTypeConstructor.getSupertypes(AbstractTypeConstructor.kt:38)
at org.jetbrains.kotlin.types.AbstractTypeConstructor$ModuleViewTypeConstructor.getSupertypes(AbstractTypeConstructor.kt:33)
at org.jetbrains.kotlin.types.TypeUtils.getImmediateSupertypes(TypeUtils.java:230)
at org.jetbrains.kotlin.types.TypeUtils.collectAllSupertypes(TypeUtils.java:255)
at org.jetbrains.kotlin.types.TypeUtils.getAllSupertypes(TypeUtils.java:268)
at org.jetbrains.kotlin.idea.util.TypeUtils.getResolvableApproximations(TypeUtils.kt:128)
at org.jetbrains.kotlin.idea.util.TypeUtils.getResolvableApproximations$default(TypeUtils.kt:126)
at org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyIntention$Companion$createTypeExpressionForTemplate$1.invoke(SpecifyTypeExplicitlyIntention.kt:133)
at org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyIntention$Companion.createTypeExpressionForTemplate(SpecifyTypeExplicitlyIntention.kt:149)
at org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyIntention$Companion.addTypeAnnotationWithTemplate(SpecifyTypeExplicitlyIntention.kt:234)
at org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyIntention$Companion.addTypeAnnotationWithTemplate$default(SpecifyTypeExplicitlyIntention.kt:229)
at org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyIntention$Companion.addTypeAnnotation(SpecifyTypeExplicitlyIntention.kt:195)
at org.jetbrains.kotlin.idea.quickfix.RemovePartsFromPropertyFix.invoke(RemovePartsFromPropertyFix.kt:82)
at org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction.invoke(KotlinQuickFixAction.kt:37)
at com.intellij.codeInsight.intention.impl.IntentionActionWithTextCaching$MyIntentionAction.invoke(IntentionActionWithTextCaching.java:179)
at com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler.lambda$invokeIntention$4(ShowIntentionActionsHandler.java
See QF tests like QuickFixTestGenerated$RemoveRedundantInitializer.testSimple
After type refinement was introduced we sometimes may request
some additional data from ModuleDescriptor
But if it's been invalidated after first part of refactoring's been applied
requesting content may fail (see ModuleDescriptorImpl::assertValid)
See the test:
org.jetbrains.kotlin.idea.refactoring.introduce.ExtractionTestGenerated.IntroduceLambdaParameter#testLambdaParamInPrimaryConstructor
Before refinement, order of accessing compiler's entities (supertypes,
descriptors, memberscopes) was always the following:
sources -> libraries -> SDK (built-ins)
With refinement, it is sometimes possible to inverse this order, e.g.
access sources after libraries or SDK. This mainly happens in the following
scenario:
- we reference some library/SDK class, but do not acquire source-lock
This might seem a bit weird, but actually it is quite easy to achieve
as soon as we understand that analysis sources doesn't necessarily
acquires respective lock, only forcing lazy computations does so.
E.g., we can just traverse PSI tree and meet some refernce to "Any?" -
this doesn't involves acquiring source-lock
- we start resolving it, which usually involves acquiring library/SDK-lock
(e.g., in order to get it supertypes or memberScope)
- because we reference it from the source-module, we may like to refine
it, in which case we will have to acquire source-lock on refinement
cache
Obviously, that may lead to deadlocks, so, in this commit we disable
creating granular locks when we work with refinement.
Note that if refinement is disabled (which is the case for all non-MPP
projects), we still create separate locks.
After refinement is introduced it becomes possible to have a different
descriptors instances for effectively the same descriptors
Also, it accidentally fixes KT-25432 because is caused by a different
version of descriptors created for NewCapturedType
^KT-25432 Fixed
Refined types with non-trivial substitution may result in different
descriptors instances
See PsiUnifierTestGenerated$Equivalence$Expressions.testArrayAccess
Before types refinement has been introduced it was reasonable to assume
that whenever we have two callables in the same declaration
they are actually different
But it become false once types refinement were introduced
and the same declarations may appear as different descriptors' instances
when viewing from different modules
The change does look very fragile because in many cases
source element is NO_SOURCE
At the same time, declaring actually different members
with the same signature is prohibited and may make sense only
in case of source-based members
It's needed for situation when we have JS module with expect declaration,
so we refine member scope for that expect declaration and try to
compare to function descriptors (e.g. of `equals` function) from
different class descriptors (expect and actual) and report diagnostic
about name clash. So, since we can earn descriptors from type refinement,
they can be not identical but still equals, so with should use structural
equality to comparing them
Otherwise, it results in skipping refinement for JobNode when requested
from JVM module while it's necessary because CompletionHandlerBase's content
depends on the module
It's necessary when expect class is actualized via typealias
To support it properly, we need to return AbbriviatedType instead of
SimpleTypeImpl, thus scopeFactory is not enough anymore
The most interesting part happens in SimpleType.refine, other types
either don't implement refinement at all (they return just 'this',
mainly it's some special types, like ErrorType and such) or implement
it trivially via recursion (those are "composite" types)
SimpleType.refine captures so-called refinement factory, which is essentially
an injected callback which tells how to reconstruct the type with new
(refined) memberScope.
We have to inject callback because we express quite different types with
SimpleTypeImpl, and some of them need different refinement logic.
Another possible implementation approach (more invasive one) would be
to extract those types in separate subtypes of KotlinType and implement
'refine' via overrides.
The most meaningful callbacks are injected from
'AbstractClassDescriptor.defaultType' and from 'KotlinTypeFactory'.
This commit introduces TypeConstructor.refine method.
It's implementation can be roughly split in three parts:
- trivial implementations which just return 'this': mostly, it used for
typeConstructors which can not be refined at all (e.g.
IntegerValueTypeConstructor and other special cases of constructors)
- delegating implementations which call 'refine' recursively for
component typeConstructors -- obviously, they are used in composite
typeConstructors (like IntersectionTypeConstructor)
- finally, the most interesting one is in 'AbstractTypeConstructor'
which returns lightweight wrapper called 'ModuleViewTypeConstructor'.
The idea here is to propagate refinement to supertypes without eagerly
computing them all.
VERY IMPORTANT CAVEAT of TypeConstructor.refine is that call to this
method CAN NOT add new supertypes, so returned supertypes are not
entirely "valid". See the KDoc for TypeConstructor.refine for details
- All refinement-related methods are incapsulated in
ModuleAwareClassDescriptor
- most of classes implement it trivially by retning unchanged scope
- LazyClassDescriptor and DeserializedClassDescriptor have non-trivial
implementations of the refinement-related methods
- General idea is to return new scope which captures refiner and will
later use it to get correct content of itself (currently, refiner is
unused, and will be used for that in later commits)
- In order to not repeat similar work, those new instances of scopes are
cached in ScopeHolderForClass, which is essentially a cache of form
KotlinTypeRefiner -> MemberScope