diff --git a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypes.kt b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypes.kt index 7f76aa1ef1e..56ecfd2d540 100644 --- a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypes.kt +++ b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypes.kt @@ -18,7 +18,17 @@ sealed class ConeKotlinTypeProjection : TypeArgumentMarker { } enum class ProjectionKind { - STAR, IN, OUT, INVARIANT + STAR, IN, OUT, INVARIANT; + + operator fun plus(other: ProjectionKind): ProjectionKind { + return when { + this == other -> this + this == STAR || other == STAR -> STAR + this == INVARIANT -> other + other == INVARIANT -> this + else -> STAR + } + } } object ConeStarProjection : ConeKotlinTypeProjection() { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt index 361d1e7b25b..59490b38d0a 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier import org.jetbrains.kotlin.fir.references.FirErrorNamedReference import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate +import org.jetbrains.kotlin.fir.resolve.substitution.AbstractConeSubstitutor import org.jetbrains.kotlin.fir.resolve.transformers.resultType import org.jetbrains.kotlin.fir.scopes.impl.withReplacedConeType import org.jetbrains.kotlin.fir.symbols.* @@ -46,10 +47,41 @@ fun ConeClassLikeLookupTag.toSymbol(useSiteSession: FirSession): ConeClassifierS return firSymbolProvider.getSymbolByLookupTag(this) } -fun ConeAbbreviatedType.directExpansionType(useSiteSession: FirSession): ConeClassLikeType? = - abbreviationLookupTag +fun ConeAbbreviatedType.directExpansionType(useSiteSession: FirSession): ConeClassLikeType? { + val typeAlias = abbreviationLookupTag .toSymbol(useSiteSession) - ?.safeAs()?.fir?.expandedConeType + ?.safeAs()?.fir ?: return null + + val resultType = typeAlias.expandedConeType ?: return null + if (resultType.typeArguments.isEmpty()) return resultType + return mapTypeAliasArguments(typeAlias, this, resultType) as? ConeClassLikeType +} + +private fun mapTypeAliasArguments(typeAlias: FirTypeAlias, abbreviatedType: ConeAbbreviatedType, resultingType: ConeClassLikeType): ConeKotlinType { + val typeAliasMap = typeAlias.typeParameters.map { it.symbol }.zip(abbreviatedType.typeArguments).toMap() + + val substitutor = object : AbstractConeSubstitutor() { + override fun substituteType(type: ConeKotlinType): ConeKotlinType? { + return null + } + + override fun substituteArgument(projection: ConeKotlinTypeProjection): ConeKotlinTypeProjection? { + val type = (projection as? ConeTypedProjection)?.type ?: return null + val symbol = (type as? ConeTypeParameterType)?.lookupTag?.toSymbol() ?: return super.substituteArgument(projection) + val mappedProjection = typeAliasMap[symbol] ?: return super.substituteArgument(projection) + val mappedType = (mappedProjection as? ConeTypedProjection)?.type ?: return mappedProjection + val resultingKind = mappedProjection.kind + projection.kind + return when (resultingKind) { + ProjectionKind.STAR -> ConeStarProjection + ProjectionKind.IN -> ConeKotlinTypeProjectionIn(mappedType) + ProjectionKind.OUT -> ConeKotlinTypeProjectionOut(mappedType) + ProjectionKind.INVARIANT -> mappedType + } + } + } + + return substitutor.substituteOrSelf(resultingType) +} fun ConeClassifierLookupTag.toSymbol(useSiteSession: FirSession): ConeClassifierSymbol? = when (this) { @@ -137,7 +169,7 @@ fun T.withNullability(nullability: ConeNullability): T { } -fun T.withArguments(arguments: Array): T { +fun T.withArguments(arguments: Array): T { if (this.typeArguments === arguments) { return this } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/substitution/ConeSubstitutor.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/substitution/ConeSubstitutor.kt index ba997960aa9..073fd2bd697 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/substitution/ConeSubstitutor.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/substitution/ConeSubstitutor.kt @@ -34,7 +34,7 @@ fun ConeSubstitutor.substituteOrNull(type: ConeKotlinType?): ConeKotlinType? { } abstract class AbstractConeSubstitutor : ConeSubstitutor() { - private fun wrapProjection(old: ConeKotlinTypeProjection, newType: ConeKotlinType): ConeKotlinTypeProjection { + protected fun wrapProjection(old: ConeKotlinTypeProjection, newType: ConeKotlinType): ConeKotlinTypeProjection { return when (old) { is ConeStarProjection -> old is ConeKotlinTypeProjectionIn -> ConeKotlinTypeProjectionIn(newType) @@ -45,6 +45,11 @@ abstract class AbstractConeSubstitutor : ConeSubstitutor() { } abstract fun substituteType(type: ConeKotlinType): ConeKotlinType? + open fun substituteArgument(projection: ConeKotlinTypeProjection): ConeKotlinTypeProjection? { + val type = (projection as? ConeTypedProjection)?.type ?: return null + val newType = substituteOrNull(type) ?: return null + return wrapProjection(projection, newType) + } fun makeNullableIfNeed(isNullable: Boolean, type: ConeKotlinType?): ConeKotlinType? { if (!isNullable) return type @@ -93,11 +98,8 @@ abstract class AbstractConeSubstitutor : ConeSubstitutor() { val newArguments by lazy { arrayOfNulls(typeArguments.size) } var initialized = false for ((index, typeArgument) in this.typeArguments.withIndex()) { - val type = (typeArgument as? ConeTypedProjection)?.type ?: continue - val newType = substituteOrNull(type) - if (newType != null) { + newArguments[index] = substituteArgument(typeArgument)?.also { initialized = true - newArguments[index] = wrapProjection(typeArgument, newType) } } diff --git a/compiler/fir/resolve/testData/resolve/stdlib/simpleDelegatedToMap.txt b/compiler/fir/resolve/testData/resolve/stdlib/simpleDelegatedToMap.txt index a64f00c433c..7414c3644d6 100644 --- a/compiler/fir/resolve/testData/resolve/stdlib/simpleDelegatedToMap.txt +++ b/compiler/fir/resolve/testData/resolve/stdlib/simpleDelegatedToMap.txt @@ -16,10 +16,10 @@ FILE: simpleDelegatedToMap.kt } } - public final var bar: by R|kotlin/collections/hashMapOf|() - public get(): { - ^ D|/bar|.#(Null(null), ::R|/bar|) + public final var bar: by R|kotlin/collections/hashMapOf|() + public get(): { + ^ D|/bar|.#(Null(null), ::R|/bar|) } - public set(: ): R|kotlin/Unit| { - D|/bar|.#(Null(null), ::R|/bar|, R|/|) + public set(: ): R|kotlin/Unit| { + D|/bar|.#(Null(null), ::R|/bar|, R|/|) } diff --git a/compiler/fir/resolve/testData/resolve/typeAliasWithTypeArguments.kt b/compiler/fir/resolve/testData/resolve/typeAliasWithTypeArguments.kt new file mode 100644 index 00000000000..1e83b6eff6b --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/typeAliasWithTypeArguments.kt @@ -0,0 +1,94 @@ +interface A { + fun foo() +} + +interface B { + fun bar() +} + +interface C { + fun baz() +} + +interface Inv() { + fun k(): K + fun t(): T +} + +typealias Inv0 = Inv +typealias Inv1 = Inv +typealias Inv2 = Inv +typealias Inv3 = Inv + +fun testBase(inv: Inv) { + inv.k() + inv.t() +} + +fun test_0(inv: Inv0) { + inv.k().foo() + inv.t().bar() +} + +fun test_1(inv: Inv1) { + inv.k().foo() + inv.t().bar() +} + +fun test_2(inv: Inv2) { + inv.k().foo() + inv.t().bar() +} + +fun test_3(inv: Inv3) { + inv.k().foo() + inv.t().bar() + inv.t().baz() +} + +typealias Inv02 = Inv, C1> + +fun test_4(inv: Inv02) { + inv.k().k().foo() + inv.k().t().bar() + inv.t().baz() +} + +interface In { + fun take(x: T) +} +interface Out { + fun value(): T +} + +interface Invariant { + fun take(x: T) + fun value(): T +} + +typealias In1 = In +typealias Out1 = Out +typealias Invariant1 = Invariant + + +fun test_5(a: A, in1: In1, in2: In1, in3: In1) { + in1.take(a) + in2.take(a) + in3.take(a) +} + +fun test_6(a: A, out1: Out1, out2: Out1, out3: Out1) { + out1.value().foo() + out2.value().foo() + out3.value().foo() +} + +fun test_7(a: A, inv1: Invariant1, inv2: Invariant1, inv3: Invariant1) { + inv1.value().foo() + inv2.value().foo() + inv3.value().foo() + + inv1.take(a) + inv2.take(a) + inv3.take(a) +} \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/typeAliasWithTypeArguments.txt b/compiler/fir/resolve/testData/resolve/typeAliasWithTypeArguments.txt new file mode 100644 index 00000000000..0e9bae266bf --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/typeAliasWithTypeArguments.txt @@ -0,0 +1,85 @@ +FILE: typeAliasWithTypeArguments.kt + public abstract interface A : R|kotlin/Any| { + public abstract fun foo(): R|kotlin/Unit| + + } + public abstract interface B : R|kotlin/Any| { + public abstract fun bar(): R|kotlin/Unit| + + } + public abstract interface C : R|kotlin/Any| { + public abstract fun baz(): R|kotlin/Unit| + + } + public abstract interface Inv : R|kotlin/Any| { + public abstract fun k(): R|K| + + public abstract fun t(): R|T| + + } + public final typealias Inv0 = R|Inv| + public final typealias Inv1 = R|Inv| + public final typealias Inv2 = R|Inv| + public final typealias Inv3 = R|Inv| + public final fun testBase(inv: R|Inv|): R|kotlin/Unit| { + R|/inv|.R|FakeOverride|() + R|/inv|.R|FakeOverride|() + } + public final fun test_0(inv: R|Inv0|): R|kotlin/Unit| { + R|/inv|.R|FakeOverride|().R|/A.foo|() + R|/inv|.R|FakeOverride|().R|/B.bar|() + } + public final fun test_1(inv: R|Inv1|): R|kotlin/Unit| { + R|/inv|.R|FakeOverride|().R|/A.foo|() + R|/inv|.R|FakeOverride|().R|/B.bar|() + } + public final fun test_2(inv: R|Inv2|): R|kotlin/Unit| { + R|/inv|.R|FakeOverride|().R|/A.foo|() + R|/inv|.R|FakeOverride|().R|/B.bar|() + } + public final fun test_3(inv: R|Inv3|): R|kotlin/Unit| { + R|/inv|.R|FakeOverride|().R|/A.foo|() + R|/inv|.R|FakeOverride|().#() + R|/inv|.R|FakeOverride|().R|/C.baz|() + } + public final typealias Inv02 = R|Inv, C1>| + public final fun test_4(inv: R|Inv02|): R|kotlin/Unit| { + R|/inv|.R|FakeOverride|>|().R|FakeOverride|().R|/A.foo|() + R|/inv|.R|FakeOverride|>|().R|FakeOverride|().R|/B.bar|() + R|/inv|.R|FakeOverride|().R|/C.baz|() + } + public abstract interface In : R|kotlin/Any| { + public abstract fun take(x: R|T|): R|kotlin/Unit| + + } + public abstract interface Out : R|kotlin/Any| { + public abstract fun value(): R|T| + + } + public abstract interface Invariant : R|kotlin/Any| { + public abstract fun take(x: R|T|): R|kotlin/Unit| + + public abstract fun value(): R|T| + + } + public final typealias In1 = R|In| + public final typealias Out1 = R|Out| + public final typealias Invariant1 = R|Invariant| + public final fun test_5(a: R|A|, in1: R|In1|, in2: R|In1|, in3: R|In1|): R|kotlin/Unit| { + R|/in1|.R|FakeOverride|(R|/a|) + R|/in2|.R|FakeOverride|(R|/a|) + R|/in3|.R|FakeOverride|(R|/a|) + } + public final fun test_6(a: R|A|, out1: R|Out1|, out2: R|Out1|, out3: R|Out1|): R|kotlin/Unit| { + R|/out1|.R|FakeOverride|().R|/A.foo|() + R|/out2|.R|FakeOverride|().R|/A.foo|() + R|/out3|.R|FakeOverride|().R|/A.foo|() + } + public final fun test_7(a: R|A|, inv1: R|Invariant1|, inv2: R|Invariant1|, inv3: R|Invariant1|): R|kotlin/Unit| { + R|/inv1|.R|FakeOverride|().R|/A.foo|() + R|/inv2|.R|FakeOverride|().R|/A.foo|() + R|/inv3|.R|FakeOverride|().R|/A.foo|() + R|/inv1|.R|FakeOverride|(R|/a|) + R|/inv2|.R|FakeOverride|(R|/a|) + R|/inv3|.R|FakeOverride|(R|/a|) + } diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseGenerated.java index e8875636a93..2be4be3193d 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseGenerated.java @@ -144,6 +144,11 @@ public class FirResolveTestCaseGenerated extends AbstractFirResolveTestCase { runTest("compiler/fir/resolve/testData/resolve/typeAliasWithGeneric.kt"); } + @TestMetadata("typeAliasWithTypeArguments.kt") + public void testTypeAliasWithTypeArguments() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/typeAliasWithTypeArguments.kt"); + } + @TestMetadata("typeFromGetter.kt") public void testTypeFromGetter() throws Exception { runTest("compiler/fir/resolve/testData/resolve/typeFromGetter.kt"); diff --git a/compiler/testData/ir/irText/declarations/classLevelProperties.fir.txt b/compiler/testData/ir/irText/declarations/classLevelProperties.fir.txt index e8c25d2d258..73658efd2c5 100644 --- a/compiler/testData/ir/irText/declarations/classLevelProperties.fir.txt +++ b/compiler/testData/ir/irText/declarations/classLevelProperties.fir.txt @@ -109,7 +109,7 @@ FILE fqName: fileName:/classLevelProperties.kt $this: VALUE_PARAMETER name: type:.C BLOCK_BODY RETURN type=kotlin.Nothing from='public final fun (): IrErrorType declared in .C' - ERROR_CALL 'Unresolved reference: #' type=IrErrorType + ERROR_CALL 'Unresolved reference: #' type=IrErrorType ERROR_CALL 'Unresolved reference: this#' type=.C PROPERTY_REFERENCE 'public final test8: IrErrorType [delegated,var]' field='FIELD DELEGATE name:test8$delegate type:java.util.HashMap visibility:private [final]' getter=null setter=null type=kotlin.reflect.KProperty<*> origin=null FUN DELEGATED_PROPERTY_ACCESSOR name: visibility:public modality:FINAL <> ($this:.C, :IrErrorType) returnType:kotlin.Unit @@ -117,7 +117,7 @@ FILE fqName: fileName:/classLevelProperties.kt $this: VALUE_PARAMETER name: type:.C VALUE_PARAMETER name: index:0 type:IrErrorType BLOCK_BODY - ERROR_CALL 'Unresolved reference: #' type=IrErrorType + ERROR_CALL 'Unresolved reference: #' type=IrErrorType ERROR_CALL 'Unresolved reference: this#' type=.C PROPERTY_REFERENCE 'public final test8: IrErrorType [delegated,var]' field='FIELD DELEGATE name:test8$delegate type:java.util.HashMap visibility:private [final]' getter='public final fun (): IrErrorType declared in .C' setter=null type=kotlin.reflect.KProperty<*> origin=null GET_VAR ': IrErrorType declared in .C.' type=IrErrorType origin=null diff --git a/compiler/testData/ir/irText/declarations/delegatedProperties.fir.txt b/compiler/testData/ir/irText/declarations/delegatedProperties.fir.txt index 12154fd7732..115d8e4892a 100644 --- a/compiler/testData/ir/irText/declarations/delegatedProperties.fir.txt +++ b/compiler/testData/ir/irText/declarations/delegatedProperties.fir.txt @@ -93,14 +93,14 @@ FILE fqName: fileName:/delegatedProperties.kt correspondingProperty: PROPERTY name:test4 visibility:public modality:FINAL [delegated,var] BLOCK_BODY RETURN type=kotlin.Nothing from='public final fun (): IrErrorType declared in ' - ERROR_CALL 'Unresolved reference: #' type=IrErrorType + ERROR_CALL 'Unresolved reference: #' type=IrErrorType CONST Null type=kotlin.Nothing? value=null PROPERTY_REFERENCE 'public final test4: IrErrorType [delegated,var]' field='FIELD DELEGATE name:test4$delegate type:java.util.HashMap visibility:private [final,static]' getter=null setter=null type=kotlin.reflect.KProperty<*> origin=null FUN DELEGATED_PROPERTY_ACCESSOR name: visibility:public modality:FINAL <> (:IrErrorType) returnType:kotlin.Unit correspondingProperty: PROPERTY name:test4 visibility:public modality:FINAL [delegated,var] VALUE_PARAMETER name: index:0 type:IrErrorType BLOCK_BODY - ERROR_CALL 'Unresolved reference: #' type=IrErrorType + ERROR_CALL 'Unresolved reference: #' type=IrErrorType CONST Null type=kotlin.Nothing? value=null PROPERTY_REFERENCE 'public final test4: IrErrorType [delegated,var]' field='FIELD DELEGATE name:test4$delegate type:java.util.HashMap visibility:private [final,static]' getter='public final fun (): IrErrorType declared in ' setter=null type=kotlin.reflect.KProperty<*> origin=null GET_VAR ': IrErrorType declared in .' type=IrErrorType origin=null diff --git a/compiler/testData/ir/irText/declarations/packageLevelProperties.fir.txt b/compiler/testData/ir/irText/declarations/packageLevelProperties.fir.txt index 6fb2373d375..e01bdb9c120 100644 --- a/compiler/testData/ir/irText/declarations/packageLevelProperties.fir.txt +++ b/compiler/testData/ir/irText/declarations/packageLevelProperties.fir.txt @@ -86,14 +86,14 @@ FILE fqName: fileName:/packageLevelProperties.kt correspondingProperty: PROPERTY name:test8 visibility:public modality:FINAL [delegated,var] BLOCK_BODY RETURN type=kotlin.Nothing from='public final fun (): IrErrorType declared in ' - ERROR_CALL 'Unresolved reference: #' type=IrErrorType + ERROR_CALL 'Unresolved reference: #' type=IrErrorType CONST Null type=kotlin.Nothing? value=null PROPERTY_REFERENCE 'public final test8: IrErrorType [delegated,var]' field='FIELD DELEGATE name:test8$delegate type:java.util.HashMap visibility:private [final,static]' getter=null setter=null type=kotlin.reflect.KProperty<*> origin=null FUN DELEGATED_PROPERTY_ACCESSOR name: visibility:public modality:FINAL <> (:IrErrorType) returnType:kotlin.Unit correspondingProperty: PROPERTY name:test8 visibility:public modality:FINAL [delegated,var] VALUE_PARAMETER name: index:0 type:IrErrorType BLOCK_BODY - ERROR_CALL 'Unresolved reference: #' type=IrErrorType + ERROR_CALL 'Unresolved reference: #' type=IrErrorType CONST Null type=kotlin.Nothing? value=null PROPERTY_REFERENCE 'public final test8: IrErrorType [delegated,var]' field='FIELD DELEGATE name:test8$delegate type:java.util.HashMap visibility:private [final,static]' getter='public final fun (): IrErrorType declared in ' setter=null type=kotlin.reflect.KProperty<*> origin=null GET_VAR ': IrErrorType declared in .' type=IrErrorType origin=null