Skip to content

Commit

Permalink
Self types: Add Self type for class annotated with @self into all
Browse files Browse the repository at this point in the history
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, *>>
```
  • Loading branch information
maxim092001 committed Jan 7, 2023
1 parent c71098e commit 3c4ce65
Show file tree
Hide file tree
Showing 13 changed files with 186 additions and 26 deletions.
29 changes: 17 additions & 12 deletions compiler/fir/analysis-tests/testData/resolve/cfg/selfTypes.fir.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
FILE: selfTypes.kt
@R|kotlin/Self|() public final class JustSelfAnnotation<out <Self> : R|JustSelfAnnotation<<Self>>|> : R|kotlin/Any| {
public constructor(): R|JustSelfAnnotation| {
public constructor<out <Self> : R|JustSelfAnnotation<<Self>>|>(): R|JustSelfAnnotation<<Self>>| {
super<R|kotlin/Any|>()
}

Expand All @@ -10,7 +10,7 @@ FILE: selfTypes.kt

}
@R|kotlin/Self|() public final class ReturnType<out <Self> : R|ReturnType<<Self>>|> : R|kotlin/Any| {
public constructor(): R|ReturnType| {
public constructor<out <Self> : R|ReturnType<<Self>>|>(): R|ReturnType<<Self>>| {
super<R|kotlin/Any|>()
}

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

}
@R|kotlin/Self|() public final class ReturnTypeWithTypeParameter<T, out <Self> : R|ReturnTypeWithTypeParameter<T, <Self>>|> : R|kotlin/Any| {
public constructor<T>(): R|ReturnTypeWithTypeParameter<T>| {
public constructor<T, out <Self> : R|ReturnTypeWithTypeParameter<T, <Self>>|>(): R|ReturnTypeWithTypeParameter<T, <Self>>| {
super<R|kotlin/Any|>()
}

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

public final fun functionWithConstruct(): R|<Self>| {
lval s: R|ReturnTypeWithTypeParameter<kotlin/Int, ReturnTypeWithTypeParameter<kotlin/Int, *>>| = R|/ReturnTypeWithTypeParameter.ReturnTypeWithTypeParameter|<R|kotlin/Int|, R|ReturnTypeWithTypeParameter<kotlin/Int, *>|>()
^functionWithConstruct (this@R|/ReturnTypeWithTypeParameter| as R|<Self>|)
}

}
@R|kotlin/Self|() public final class ReturnTypeWithTypeParameters<T, A, F, out <Self> : R|ReturnTypeWithTypeParameters<T, A, F, <Self>>|> : R|kotlin/Any| {
public constructor<T, A, F>(): R|ReturnTypeWithTypeParameters<T, A, F>| {
public constructor<T, A, F, out <Self> : R|ReturnTypeWithTypeParameters<T, A, F, <Self>>|>(): R|ReturnTypeWithTypeParameters<T, A, F, <Self>>| {
super<R|kotlin/Any|>()
}

Expand All @@ -46,7 +51,7 @@ FILE: selfTypes.kt
}

@R|kotlin/Self|() public final inner class Inner<out <Self> : R|InnerClass.Inner<<Self>>|> : R|kotlin/Any| {
public InnerClass.constructor(): R|InnerClass.Inner| {
public InnerClass.constructor<out <Self> : R|InnerClass.Inner<<Self>>|>(): R|InnerClass.Inner<<Self>>| {
super<R|kotlin/Any|>()
}

Expand All @@ -63,7 +68,7 @@ FILE: selfTypes.kt
}

@R|kotlin/Self|() public final class Nested<out <Self> : R|NestedClass.Nested<<Self>>|> : R|kotlin/Any| {
public constructor(): R|NestedClass.Nested| {
public constructor<out <Self> : R|NestedClass.Nested<<Self>>|>(): R|NestedClass.Nested<<Self>>| {
super<R|kotlin/Any|>()
}

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

}
@R|kotlin/Self|() public final class InnerSelfClass<out <Self> : R|InnerSelfClass<<Self>>|> : R|kotlin/Any| {
public constructor(): R|InnerSelfClass| {
public constructor<out <Self> : R|InnerSelfClass<<Self>>|>(): R|InnerSelfClass<<Self>>| {
super<R|kotlin/Any|>()
}

Expand All @@ -95,12 +100,12 @@ FILE: selfTypes.kt
}

public final fun returnSelfClassType(): R|InnerSelfClass.Self| {
^returnSelfClassType R|/InnerSelfClass.InnerSelfClass|().R|/InnerSelfClass.Self.Self|()
^returnSelfClassType R|/InnerSelfClass.InnerSelfClass|<R|InnerSelfClass<*>|>().R|/InnerSelfClass.Self.Self|()
}

}
@R|kotlin/Self|() public final class TypeAliasSelf<out <Self> : R|TypeAliasSelf<<Self>>|> : R|kotlin/Any| {
public constructor(): R|TypeAliasSelf| {
public constructor<out <Self> : R|TypeAliasSelf<<Self>>|>(): R|TypeAliasSelf<<Self>>| {
super<R|kotlin/Any|>()
}

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

}
@R|kotlin/Self|() public final class SelfWithSelfVariable<out <Self> : R|SelfWithSelfVariable<<Self>>|> : R|kotlin/Any| {
public constructor(): R|SelfWithSelfVariable| {
public constructor<out <Self> : R|SelfWithSelfVariable<<Self>>|>(): R|SelfWithSelfVariable<<Self>>| {
super<R|kotlin/Any|>()
}

Expand All @@ -132,7 +137,7 @@ FILE: selfTypes.kt
}

@R|kotlin/Self|() public final inner class SelfAnnotated<S : R|InnerClassWithSelfAnnotation<S>|, out <Self> : R|InnerClassWithSelfAnnotation.SelfAnnotated<S, <Self>>|> : R|kotlin/Any| {
public InnerClassWithSelfAnnotation<S>.constructor(): R|InnerClassWithSelfAnnotation.SelfAnnotated<S>| {
public InnerClassWithSelfAnnotation<S>.constructor<out <Self> : R|InnerClassWithSelfAnnotation.SelfAnnotated<S, <Self>>|>(): R|InnerClassWithSelfAnnotation.SelfAnnotated<S, <Self>>| {
super<R|kotlin/Any|>()
}

Expand All @@ -150,7 +155,7 @@ FILE: selfTypes.kt
@R|kotlin/Self|() public abstract interface SelfTypeParameterInterface<out <Self> : R|SelfTypeParameterInterface<<Self>>|> : R|kotlin/Any| {
}
@R|kotlin/Self|() public final class SelfTypeAsTypeParameterInExtends<out <Self> : R|SelfTypeAsTypeParameterInExtends<<Self>>|> : R|SelfTypeParameterInterface<<Self>>| {
public constructor(): R|SelfTypeAsTypeParameterInExtends| {
public constructor<out <Self> : R|SelfTypeAsTypeParameterInExtends<<Self>>|>(): R|SelfTypeAsTypeParameterInExtends<<Self>>| {
super<R|kotlin/Any|>()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ class ReturnTypeWithTypeParameter<T> {
fun returnType(): Self {
return this as Self
}

fun functionWithConstruct(): Self {
val s = ReturnTypeWithTypeParameter<Int, ReturnTypeWithTypeParameter<Int, *>>()
return this as Self
}
}

@Self
Expand Down Expand Up @@ -58,7 +63,7 @@ class InnerSelfClass {
}

fun returnSelfClassType(): InnerSelfClass.Self {
return InnerSelfClass().Self()
return InnerSelfClass<InnerSelfClass<*>>().Self()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ class Fir2IrClassifierStorage(
typeContext: ConversionTypeContext = ConversionTypeContext.DEFAULT
) {
typeParameters = owner.typeParameters.mapIndexedNotNull { index, typeParameter ->
if (typeParameter !is FirTypeParameter) return@mapIndexedNotNull null
if (typeParameter !is FirTypeParameter || (typeParameter.name == SELF_TYPE && owner is FirConstructor)) return@mapIndexedNotNull null
getIrTypeParameter(typeParameter, index, symbol, typeContext).apply {
parent = this@setTypeParameters
if (superTypes.isEmpty()) {
Expand Down Expand Up @@ -401,7 +401,7 @@ class Fir2IrClassifierStorage(
) { symbol ->
irFactory.createTypeParameter(
startOffset, endOffset, origin, symbol,
name, if (index < 0) 0 else index,
replacedSelfName, if (index < 0) 0 else index,
isReified,
variance
)
Expand All @@ -413,7 +413,7 @@ class Fir2IrClassifierStorage(
) { symbol ->
irFactory.createTypeParameter(
startOffset, endOffset, origin, symbol,
name, if (index < 0) 0 else index,
replacedSelfName, if (index < 0) 0 else index,
isReified,
variance
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1440,8 +1440,14 @@ class Fir2IrDeclarationStorage(
firPropertySymbol.dispatchReceiverClassLookupTagOrNull() !=
firPropertySymbol.originalForSubstitutionOverride?.dispatchReceiverClassLookupTagOrNull()
Fir2IrLazyProperty(
components, startOffset, endOffset, declarationOrigin,
fir, (lazyParent as? Fir2IrLazyClass)?.fir, symbol, isFakeOverride
components,
startOffset,
endOffset,
declarationOrigin,
fir,
(lazyParent as? Fir2IrLazyClass)?.fir,
symbol,
isFakeOverride
).apply {
this.parent = lazyParent
}
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,19 @@ import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
import org.jetbrains.kotlin.fir.declarations.FirTypeParameterRefsOwner
import org.jetbrains.kotlin.fir.declarations.hasAnnotation
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.ConeTypeIntersector
import org.jetbrains.kotlin.fir.types.FirTypeProjection
import org.jetbrains.kotlin.fir.types.builder.buildPlaceholderProjection
import org.jetbrains.kotlin.fir.types.builder.buildTypeProjectionWithVariance
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.fir.types.builder.buildTypeProjectionWithVariance
import org.jetbrains.kotlin.fir.types.isRaw
import org.jetbrains.kotlin.fir.types.toFirResolvedTypeRef
import org.jetbrains.kotlin.types.Variance
Expand All @@ -33,7 +42,7 @@ sealed class TypeArgumentMapping {

internal object MapTypeArguments : ResolutionStage() {
override suspend fun check(candidate: Candidate, callInfo: CallInfo, sink: CheckerSink, context: ResolutionContext) {
val typeArguments = callInfo.typeArguments
val typeArguments = enrichedTypeArgumentsWithSelfType(candidate, callInfo, context, callInfo.typeArguments)
val owner = candidate.symbol.fir as FirTypeParameterRefsOwner

if (typeArguments.isEmpty()) {
Expand All @@ -60,6 +69,59 @@ internal object MapTypeArguments : ResolutionStage() {
}
}

private fun enrichedTypeArgumentsWithSelfType(
candidate: Candidate,
callInfo: CallInfo,
context: ResolutionContext,
typeArguments: List<FirTypeProjection>
): List<FirTypeProjection> {
if (candidate.symbol is FirConstructorSymbol) {

val firConstructorSymbol = candidate.symbol

if (firConstructorSymbol.callableId.classId != null) {

val classLikeSymbol = context.session.symbolProvider.getClassLikeSymbolByClassId(firConstructorSymbol.callableId.classId!!)

if (classLikeSymbol != null) {
val firClassSymbol = classLikeSymbol as FirClassSymbol

val isSelf = firClassSymbol.hasAnnotation(StandardClassIds.Annotations.Self)

if (isSelf && callInfo.callKind is CallKind.Function) {
val constructorTypeParametersSize = firConstructorSymbol.typeParameterSymbols.size
val callTypeArgumentsSize = typeArguments.size

if (constructorTypeParametersSize != callTypeArgumentsSize) {
val coneArgumentsWithStar =
(typeArguments.map { it.toConeTypeProjection() }.toList() + ConeStarProjection).toTypedArray()
val typeRef = firConstructorSymbol.fir.returnTypeRef
val oldConeType = typeRef.coneType as ConeClassLikeType
val coneTypeWithStar =
ConeClassLikeTypeImpl(
oldConeType.lookupTag,
coneArgumentsWithStar,
oldConeType.isNullable,
oldConeType.attributes
)

val typeRefWithStar = typeRef.withReplacedConeType(coneTypeWithStar)
val baseClassStarProjection = buildTypeProjectionWithVariance {
source = typeRefWithStar.source
this.typeRef = typeRefWithStar
variance = Variance.INVARIANT
}

return typeArguments + baseClassStarProjection
}
}
}

}
}
return typeArguments
}

private fun computeDefaultMappingForRawTypeMember(
owner: FirTypeParameterRefsOwner,
context: ResolutionContext
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class FirCallCompleter(
mayBeCoercionToUnitApplied = false,
expectedTypeMismatchIsReportedInChecker,
isFromCast = false,
shouldEnforceExpectedType = true,
shouldEnforceExpectedType = true
)

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

val reference = call.calleeReference as? FirNamedReferenceWithCandidate ?: return CompletionResult(call, true)

val candidate = reference.candidate
val initialType = components.initialTypeOfCandidate(candidate, call)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,15 @@ open class FirSupertypeResolverVisitor(
}.build()

firClass.replaceTypeParameters(params + selfTypeParameter)

if (firClass is FirClass) {
firClass.declarations.filterIsInstance<FirConstructor>().forEach {
val constructorTypeParams = it.typeParameters
it.replaceTypeParameters(constructorTypeParams + selfTypeParameter)
val firClassTypeRef = it.returnTypeRef.resolvedTypeFromPrototype(firClass.defaultType())
it.replaceReturnTypeRef(firClassTypeRef)
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import org.jetbrains.kotlin.fir.declarations.utils.isCompanion
import org.jetbrains.kotlin.fir.declarations.utils.isInner
import org.jetbrains.kotlin.fir.expressions.FirCallableReferenceAccess
import org.jetbrains.kotlin.fir.expressions.FirWhenExpression
import org.jetbrains.kotlin.fir.expressions.classId
import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitExtensionReceiverValue
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitReceiverValue
Expand Down Expand Up @@ -458,7 +457,9 @@ class BodyResolveContext(
.addNonLocalScopeIfNotNull(selfTypeScope)

val scopeForConstructorHeader =
staticsAndCompanion.addNonLocalScopeIfNotNull(typeParameterScope)
staticsAndCompanion
.addNonLocalScopeIfNotNull(typeParameterScope)
.addNonLocalScopeIfNotNull(selfTypeScope)

/*
* Scope for enum entries is equal to initial scope for constructor header
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,19 @@ package org.jetbrains.kotlin.fir.resolve.inference
import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.fir.types.ConeTypeVariable
import org.jetbrains.kotlin.name.SpecialNames

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

class ConeTypeParameterBasedTypeVariable(
val typeParameterSymbol: FirTypeParameterSymbol
) : ConeTypeVariable(typeParameterSymbol.name.identifier, typeParameterSymbol.toLookupTag())
) : ConeTypeVariable(
if (typeParameterSymbol.name == SpecialNames.SELF_TYPE) SELF_IDENTIFIER else typeParameterSymbol.name.identifier,
typeParameterSymbol.toLookupTag()
) {
companion object {
const val SELF_IDENTIFIER = "\$Self"
}
}
Loading

0 comments on commit 3c4ce65

Please sign in to comment.