Skip to content

Commit 3c4ce65

Browse files
committed
Self types: Add Self type for class annotated with @self into all
constructors of this class. This allows to create Self classes. Example: ``` @self class Foo<T> { // generated type: out Self: Foo<T, Self> ... } Foo<Int>() // last type argument would be generated as Foo<Foo<Int, *>> ```
1 parent c71098e commit 3c4ce65

File tree

13 files changed

+186
-26
lines changed

13 files changed

+186
-26
lines changed

compiler/fir/analysis-tests/testData/resolve/cfg/selfTypes.fir.txt

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
FILE: selfTypes.kt
22
@R|kotlin/Self|() public final class JustSelfAnnotation<out <Self> : R|JustSelfAnnotation<<Self>>|> : R|kotlin/Any| {
3-
public constructor(): R|JustSelfAnnotation| {
3+
public constructor<out <Self> : R|JustSelfAnnotation<<Self>>|>(): R|JustSelfAnnotation<<Self>>| {
44
super<R|kotlin/Any|>()
55
}
66

@@ -10,7 +10,7 @@ FILE: selfTypes.kt
1010

1111
}
1212
@R|kotlin/Self|() public final class ReturnType<out <Self> : R|ReturnType<<Self>>|> : R|kotlin/Any| {
13-
public constructor(): R|ReturnType| {
13+
public constructor<out <Self> : R|ReturnType<<Self>>|>(): R|ReturnType<<Self>>| {
1414
super<R|kotlin/Any|>()
1515
}
1616

@@ -21,17 +21,22 @@ FILE: selfTypes.kt
2121

2222
}
2323
@R|kotlin/Self|() public final class ReturnTypeWithTypeParameter<T, out <Self> : R|ReturnTypeWithTypeParameter<T, <Self>>|> : R|kotlin/Any| {
24-
public constructor<T>(): R|ReturnTypeWithTypeParameter<T>| {
24+
public constructor<T, out <Self> : R|ReturnTypeWithTypeParameter<T, <Self>>|>(): R|ReturnTypeWithTypeParameter<T, <Self>>| {
2525
super<R|kotlin/Any|>()
2626
}
2727

2828
public final fun returnType(): R|<Self>| {
2929
^returnType (this@R|/ReturnTypeWithTypeParameter| as R|<Self>|)
3030
}
3131

32+
public final fun functionWithConstruct(): R|<Self>| {
33+
lval s: R|ReturnTypeWithTypeParameter<kotlin/Int, ReturnTypeWithTypeParameter<kotlin/Int, *>>| = R|/ReturnTypeWithTypeParameter.ReturnTypeWithTypeParameter|<R|kotlin/Int|, R|ReturnTypeWithTypeParameter<kotlin/Int, *>|>()
34+
^functionWithConstruct (this@R|/ReturnTypeWithTypeParameter| as R|<Self>|)
35+
}
36+
3237
}
3338
@R|kotlin/Self|() public final class ReturnTypeWithTypeParameters<T, A, F, out <Self> : R|ReturnTypeWithTypeParameters<T, A, F, <Self>>|> : R|kotlin/Any| {
34-
public constructor<T, A, F>(): R|ReturnTypeWithTypeParameters<T, A, F>| {
39+
public constructor<T, A, F, out <Self> : R|ReturnTypeWithTypeParameters<T, A, F, <Self>>|>(): R|ReturnTypeWithTypeParameters<T, A, F, <Self>>| {
3540
super<R|kotlin/Any|>()
3641
}
3742

@@ -46,7 +51,7 @@ FILE: selfTypes.kt
4651
}
4752

4853
@R|kotlin/Self|() public final inner class Inner<out <Self> : R|InnerClass.Inner<<Self>>|> : R|kotlin/Any| {
49-
public InnerClass.constructor(): R|InnerClass.Inner| {
54+
public InnerClass.constructor<out <Self> : R|InnerClass.Inner<<Self>>|>(): R|InnerClass.Inner<<Self>>| {
5055
super<R|kotlin/Any|>()
5156
}
5257

@@ -63,7 +68,7 @@ FILE: selfTypes.kt
6368
}
6469

6570
@R|kotlin/Self|() public final class Nested<out <Self> : R|NestedClass.Nested<<Self>>|> : R|kotlin/Any| {
66-
public constructor(): R|NestedClass.Nested| {
71+
public constructor<out <Self> : R|NestedClass.Nested<<Self>>|>(): R|NestedClass.Nested<<Self>>| {
6772
super<R|kotlin/Any|>()
6873
}
6974

@@ -75,7 +80,7 @@ FILE: selfTypes.kt
7580

7681
}
7782
@R|kotlin/Self|() public final class InnerSelfClass<out <Self> : R|InnerSelfClass<<Self>>|> : R|kotlin/Any| {
78-
public constructor(): R|InnerSelfClass| {
83+
public constructor<out <Self> : R|InnerSelfClass<<Self>>|>(): R|InnerSelfClass<<Self>>| {
7984
super<R|kotlin/Any|>()
8085
}
8186

@@ -95,12 +100,12 @@ FILE: selfTypes.kt
95100
}
96101

97102
public final fun returnSelfClassType(): R|InnerSelfClass.Self| {
98-
^returnSelfClassType R|/InnerSelfClass.InnerSelfClass|().R|/InnerSelfClass.Self.Self|()
103+
^returnSelfClassType R|/InnerSelfClass.InnerSelfClass|<R|InnerSelfClass<*>|>().R|/InnerSelfClass.Self.Self|()
99104
}
100105

101106
}
102107
@R|kotlin/Self|() public final class TypeAliasSelf<out <Self> : R|TypeAliasSelf<<Self>>|> : R|kotlin/Any| {
103-
public constructor(): R|TypeAliasSelf| {
108+
public constructor<out <Self> : R|TypeAliasSelf<<Self>>|>(): R|TypeAliasSelf<<Self>>| {
104109
super<R|kotlin/Any|>()
105110
}
106111

@@ -116,7 +121,7 @@ FILE: selfTypes.kt
116121

117122
}
118123
@R|kotlin/Self|() public final class SelfWithSelfVariable<out <Self> : R|SelfWithSelfVariable<<Self>>|> : R|kotlin/Any| {
119-
public constructor(): R|SelfWithSelfVariable| {
124+
public constructor<out <Self> : R|SelfWithSelfVariable<<Self>>|>(): R|SelfWithSelfVariable<<Self>>| {
120125
super<R|kotlin/Any|>()
121126
}
122127

@@ -132,7 +137,7 @@ FILE: selfTypes.kt
132137
}
133138

134139
@R|kotlin/Self|() public final inner class SelfAnnotated<S : R|InnerClassWithSelfAnnotation<S>|, out <Self> : R|InnerClassWithSelfAnnotation.SelfAnnotated<S, <Self>>|> : R|kotlin/Any| {
135-
public InnerClassWithSelfAnnotation<S>.constructor(): R|InnerClassWithSelfAnnotation.SelfAnnotated<S>| {
140+
public InnerClassWithSelfAnnotation<S>.constructor<out <Self> : R|InnerClassWithSelfAnnotation.SelfAnnotated<S, <Self>>|>(): R|InnerClassWithSelfAnnotation.SelfAnnotated<S, <Self>>| {
136141
super<R|kotlin/Any|>()
137142
}
138143

@@ -150,7 +155,7 @@ FILE: selfTypes.kt
150155
@R|kotlin/Self|() public abstract interface SelfTypeParameterInterface<out <Self> : R|SelfTypeParameterInterface<<Self>>|> : R|kotlin/Any| {
151156
}
152157
@R|kotlin/Self|() public final class SelfTypeAsTypeParameterInExtends<out <Self> : R|SelfTypeAsTypeParameterInExtends<<Self>>|> : R|SelfTypeParameterInterface<<Self>>| {
153-
public constructor(): R|SelfTypeAsTypeParameterInExtends| {
158+
public constructor<out <Self> : R|SelfTypeAsTypeParameterInExtends<<Self>>|>(): R|SelfTypeAsTypeParameterInExtends<<Self>>| {
154159
super<R|kotlin/Any|>()
155160
}
156161

compiler/fir/analysis-tests/testData/resolve/cfg/selfTypes.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ class ReturnTypeWithTypeParameter<T> {
1818
fun returnType(): Self {
1919
return this as Self
2020
}
21+
22+
fun functionWithConstruct(): Self {
23+
val s = ReturnTypeWithTypeParameter<Int, ReturnTypeWithTypeParameter<Int, *>>()
24+
return this as Self
25+
}
2126
}
2227

2328
@Self
@@ -58,7 +63,7 @@ class InnerSelfClass {
5863
}
5964

6065
fun returnSelfClassType(): InnerSelfClass.Self {
61-
return InnerSelfClass().Self()
66+
return InnerSelfClass<InnerSelfClass<*>>().Self()
6267
}
6368
}
6469

compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrClassifierStorage.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ class Fir2IrClassifierStorage(
113113
typeContext: ConversionTypeContext = ConversionTypeContext.DEFAULT
114114
) {
115115
typeParameters = owner.typeParameters.mapIndexedNotNull { index, typeParameter ->
116-
if (typeParameter !is FirTypeParameter) return@mapIndexedNotNull null
116+
if (typeParameter !is FirTypeParameter || (typeParameter.name == SELF_TYPE && owner is FirConstructor)) return@mapIndexedNotNull null
117117
getIrTypeParameter(typeParameter, index, symbol, typeContext).apply {
118118
parent = this@setTypeParameters
119119
if (superTypes.isEmpty()) {
@@ -401,7 +401,7 @@ class Fir2IrClassifierStorage(
401401
) { symbol ->
402402
irFactory.createTypeParameter(
403403
startOffset, endOffset, origin, symbol,
404-
name, if (index < 0) 0 else index,
404+
replacedSelfName, if (index < 0) 0 else index,
405405
isReified,
406406
variance
407407
)
@@ -413,7 +413,7 @@ class Fir2IrClassifierStorage(
413413
) { symbol ->
414414
irFactory.createTypeParameter(
415415
startOffset, endOffset, origin, symbol,
416-
name, if (index < 0) 0 else index,
416+
replacedSelfName, if (index < 0) 0 else index,
417417
isReified,
418418
variance
419419
)

compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrDeclarationStorage.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1440,8 +1440,14 @@ class Fir2IrDeclarationStorage(
14401440
firPropertySymbol.dispatchReceiverClassLookupTagOrNull() !=
14411441
firPropertySymbol.originalForSubstitutionOverride?.dispatchReceiverClassLookupTagOrNull()
14421442
Fir2IrLazyProperty(
1443-
components, startOffset, endOffset, declarationOrigin,
1444-
fir, (lazyParent as? Fir2IrLazyClass)?.fir, symbol, isFakeOverride
1443+
components,
1444+
startOffset,
1445+
endOffset,
1446+
declarationOrigin,
1447+
fir,
1448+
(lazyParent as? Fir2IrLazyClass)?.fir,
1449+
symbol,
1450+
isFakeOverride
14451451
).apply {
14461452
this.parent = lazyParent
14471453
}

compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/TypeArgumentMapping.kt

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,19 @@ import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
99
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
1010
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
1111
import org.jetbrains.kotlin.fir.declarations.FirTypeParameterRefsOwner
12+
import org.jetbrains.kotlin.fir.declarations.hasAnnotation
13+
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
14+
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
15+
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
16+
import org.jetbrains.kotlin.fir.types.*
1217
import org.jetbrains.kotlin.fir.types.ConeTypeIntersector
1318
import org.jetbrains.kotlin.fir.types.FirTypeProjection
1419
import org.jetbrains.kotlin.fir.types.builder.buildPlaceholderProjection
1520
import org.jetbrains.kotlin.fir.types.builder.buildTypeProjectionWithVariance
21+
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
22+
import org.jetbrains.kotlin.name.StandardClassIds
23+
import org.jetbrains.kotlin.types.Variance
24+
import org.jetbrains.kotlin.fir.types.builder.buildTypeProjectionWithVariance
1625
import org.jetbrains.kotlin.fir.types.isRaw
1726
import org.jetbrains.kotlin.fir.types.toFirResolvedTypeRef
1827
import org.jetbrains.kotlin.types.Variance
@@ -33,7 +42,7 @@ sealed class TypeArgumentMapping {
3342

3443
internal object MapTypeArguments : ResolutionStage() {
3544
override suspend fun check(candidate: Candidate, callInfo: CallInfo, sink: CheckerSink, context: ResolutionContext) {
36-
val typeArguments = callInfo.typeArguments
45+
val typeArguments = enrichedTypeArgumentsWithSelfType(candidate, callInfo, context, callInfo.typeArguments)
3746
val owner = candidate.symbol.fir as FirTypeParameterRefsOwner
3847

3948
if (typeArguments.isEmpty()) {
@@ -60,6 +69,59 @@ internal object MapTypeArguments : ResolutionStage() {
6069
}
6170
}
6271

72+
private fun enrichedTypeArgumentsWithSelfType(
73+
candidate: Candidate,
74+
callInfo: CallInfo,
75+
context: ResolutionContext,
76+
typeArguments: List<FirTypeProjection>
77+
): List<FirTypeProjection> {
78+
if (candidate.symbol is FirConstructorSymbol) {
79+
80+
val firConstructorSymbol = candidate.symbol
81+
82+
if (firConstructorSymbol.callableId.classId != null) {
83+
84+
val classLikeSymbol = context.session.symbolProvider.getClassLikeSymbolByClassId(firConstructorSymbol.callableId.classId!!)
85+
86+
if (classLikeSymbol != null) {
87+
val firClassSymbol = classLikeSymbol as FirClassSymbol
88+
89+
val isSelf = firClassSymbol.hasAnnotation(StandardClassIds.Annotations.Self)
90+
91+
if (isSelf && callInfo.callKind is CallKind.Function) {
92+
val constructorTypeParametersSize = firConstructorSymbol.typeParameterSymbols.size
93+
val callTypeArgumentsSize = typeArguments.size
94+
95+
if (constructorTypeParametersSize != callTypeArgumentsSize) {
96+
val coneArgumentsWithStar =
97+
(typeArguments.map { it.toConeTypeProjection() }.toList() + ConeStarProjection).toTypedArray()
98+
val typeRef = firConstructorSymbol.fir.returnTypeRef
99+
val oldConeType = typeRef.coneType as ConeClassLikeType
100+
val coneTypeWithStar =
101+
ConeClassLikeTypeImpl(
102+
oldConeType.lookupTag,
103+
coneArgumentsWithStar,
104+
oldConeType.isNullable,
105+
oldConeType.attributes
106+
)
107+
108+
val typeRefWithStar = typeRef.withReplacedConeType(coneTypeWithStar)
109+
val baseClassStarProjection = buildTypeProjectionWithVariance {
110+
source = typeRefWithStar.source
111+
this.typeRef = typeRefWithStar
112+
variance = Variance.INVARIANT
113+
}
114+
115+
return typeArguments + baseClassStarProjection
116+
}
117+
}
118+
}
119+
120+
}
121+
}
122+
return typeArguments
123+
}
124+
63125
private fun computeDefaultMappingForRawTypeMember(
64126
owner: FirTypeParameterRefsOwner,
65127
context: ResolutionContext

compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class FirCallCompleter(
6767
mayBeCoercionToUnitApplied = false,
6868
expectedTypeMismatchIsReportedInChecker,
6969
isFromCast = false,
70-
shouldEnforceExpectedType = true,
70+
shouldEnforceExpectedType = true
7171
)
7272

7373
fun <T> completeCall(call: T, data: ResolutionMode): CompletionResult<T> where T : FirResolvable, T : FirStatement =
@@ -95,7 +95,6 @@ class FirCallCompleter(
9595
}
9696

9797
val reference = call.calleeReference as? FirNamedReferenceWithCandidate ?: return CompletionResult(call, true)
98-
9998
val candidate = reference.candidate
10099
val initialType = components.initialTypeOfCandidate(candidate, call)
101100

compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirSupertypesResolution.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,15 @@ open class FirSupertypeResolverVisitor(
489489
}.build()
490490

491491
firClass.replaceTypeParameters(params + selfTypeParameter)
492+
493+
if (firClass is FirClass) {
494+
firClass.declarations.filterIsInstance<FirConstructor>().forEach {
495+
val constructorTypeParams = it.typeParameters
496+
it.replaceTypeParameters(constructorTypeParams + selfTypeParameter)
497+
val firClassTypeRef = it.returnTypeRef.resolvedTypeFromPrototype(firClass.defaultType())
498+
it.replaceReturnTypeRef(firClassTypeRef)
499+
}
500+
}
492501
}
493502
}
494503

compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/BodyResolveContext.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import org.jetbrains.kotlin.fir.declarations.utils.isCompanion
1515
import org.jetbrains.kotlin.fir.declarations.utils.isInner
1616
import org.jetbrains.kotlin.fir.expressions.FirCallableReferenceAccess
1717
import org.jetbrains.kotlin.fir.expressions.FirWhenExpression
18-
import org.jetbrains.kotlin.fir.expressions.classId
1918
import org.jetbrains.kotlin.fir.resolve.*
2019
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitExtensionReceiverValue
2120
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitReceiverValue
@@ -458,7 +457,9 @@ class BodyResolveContext(
458457
.addNonLocalScopeIfNotNull(selfTypeScope)
459458

460459
val scopeForConstructorHeader =
461-
staticsAndCompanion.addNonLocalScopeIfNotNull(typeParameterScope)
460+
staticsAndCompanion
461+
.addNonLocalScopeIfNotNull(typeParameterScope)
462+
.addNonLocalScopeIfNotNull(selfTypeScope)
462463

463464
/*
464465
* Scope for enum entries is equal to initial scope for constructor header

compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/inference/ConeTypeVariables.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,19 @@ package org.jetbrains.kotlin.fir.resolve.inference
88
import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
99
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
1010
import org.jetbrains.kotlin.fir.types.ConeTypeVariable
11+
import org.jetbrains.kotlin.name.SpecialNames
1112

1213
class ConeTypeVariableForPostponedAtom(name: String) : ConeTypeVariable(name)
1314
class ConeTypeVariableForLambdaParameterType(name: String, val index: Int) : ConeTypeVariable(name)
1415
class ConeTypeVariableForLambdaReturnType(val argument: FirAnonymousFunction, name: String) : ConeTypeVariable(name)
1516

1617
class ConeTypeParameterBasedTypeVariable(
1718
val typeParameterSymbol: FirTypeParameterSymbol
18-
) : ConeTypeVariable(typeParameterSymbol.name.identifier, typeParameterSymbol.toLookupTag())
19+
) : ConeTypeVariable(
20+
if (typeParameterSymbol.name == SpecialNames.SELF_TYPE) SELF_IDENTIFIER else typeParameterSymbol.name.identifier,
21+
typeParameterSymbol.toLookupTag()
22+
) {
23+
companion object {
24+
const val SELF_IDENTIFIER = "\$Self"
25+
}
26+
}

0 commit comments

Comments
 (0)