FIR: handle named arguments in annotations properly

This commit is contained in:
Jinseong Jeon
2020-06-26 21:47:39 -07:00
committed by Mikhail Glukhikh
parent 4f36697737
commit c3fc524c0d
9 changed files with 53 additions and 22 deletions
@@ -446,9 +446,6 @@ internal fun FirReference.statementOrigin(): IrStatementOrigin? {
} }
} }
fun FirClass<*>.getPrimaryConstructorIfAny(): FirConstructor? =
declarations.filterIsInstance<FirConstructor>().firstOrNull()?.takeIf { it.isPrimary }
internal fun IrDeclarationParent.declareThisReceiverParameter( internal fun IrDeclarationParent.declareThisReceiverParameter(
symbolTable: SymbolTable, symbolTable: SymbolTable,
thisType: IrType, thisType: IrType,
@@ -6,10 +6,7 @@
package org.jetbrains.kotlin.fir.backend.generators package org.jetbrains.kotlin.fir.backend.generators
import org.jetbrains.kotlin.fir.backend.* import org.jetbrains.kotlin.fir.backend.*
import org.jetbrains.kotlin.fir.declarations.FirClass import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression
import org.jetbrains.kotlin.fir.psi import org.jetbrains.kotlin.fir.psi
@@ -19,6 +16,7 @@ import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.references.FirSuperReference import org.jetbrains.kotlin.fir.references.FirSuperReference
import org.jetbrains.kotlin.fir.render import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.resolve.calls.isFunctional import org.jetbrains.kotlin.fir.resolve.calls.isFunctional
import org.jetbrains.kotlin.fir.resolve.getCorrespondingConstructorReferenceOrNull
import org.jetbrains.kotlin.fir.resolve.inference.isBuiltinFunctionalType import org.jetbrains.kotlin.fir.resolve.inference.isBuiltinFunctionalType
import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
@@ -327,6 +325,7 @@ class CallAndReferenceGenerator(
val calleeReference = when (call) { val calleeReference = when (call) {
is FirFunctionCall -> call.calleeReference is FirFunctionCall -> call.calleeReference
is FirDelegatedConstructorCall -> call.calleeReference is FirDelegatedConstructorCall -> call.calleeReference
is FirAnnotationCall -> call.getCorrespondingConstructorReferenceOrNull(session)
else -> null else -> null
} as? FirResolvedNamedReference } as? FirResolvedNamedReference
val function = (calleeReference?.resolvedSymbol as? FirFunctionSymbol<*>)?.fir val function = (calleeReference?.resolvedSymbol as? FirFunctionSymbol<*>)?.fir
@@ -334,7 +333,7 @@ class CallAndReferenceGenerator(
val argumentMapping = call.argumentMapping val argumentMapping = call.argumentMapping
if (argumentMapping != null && argumentMapping.isNotEmpty()) { if (argumentMapping != null && argumentMapping.isNotEmpty()) {
if (valueParameters != null) { if (valueParameters != null) {
return applyArgumentsWithReorderingIfNeeded(argumentMapping, valueParameters) return applyArgumentsWithReorderingIfNeeded(call, argumentMapping, valueParameters)
} }
} }
for ((index, argument) in call.arguments.withIndex()) { for ((index, argument) in call.arguments.withIndex()) {
@@ -365,10 +364,15 @@ class CallAndReferenceGenerator(
} }
private fun IrCallWithIndexedArgumentsBase.applyArgumentsWithReorderingIfNeeded( private fun IrCallWithIndexedArgumentsBase.applyArgumentsWithReorderingIfNeeded(
call: FirCall,
argumentMapping: Map<FirExpression, FirValueParameter>, argumentMapping: Map<FirExpression, FirValueParameter>,
valueParameters: List<FirValueParameter>, valueParameters: List<FirValueParameter>,
): IrExpressionBase { ): IrExpressionBase {
if (needArgumentReordering(argumentMapping.values, valueParameters)) { // Assuming compile-time constants only inside annotation, we don't need a block to reorder arguments to preserve semantics.
// But, we still need to pick correct indices for named arguments.
if (call !is FirAnnotationCall &&
needArgumentReordering(argumentMapping.values, valueParameters)
) {
return IrBlockImpl(startOffset, endOffset, type, IrStatementOrigin.ARGUMENTS_REORDERING_FOR_CALL).apply { return IrBlockImpl(startOffset, endOffset, type, IrStatementOrigin.ARGUMENTS_REORDERING_FOR_CALL).apply {
for ((argument, parameter) in argumentMapping) { for ((argument, parameter) in argumentMapping) {
val parameterIndex = valueParameters.indexOf(parameter) val parameterIndex = valueParameters.indexOf(parameter)
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.references.FirSuperReference import org.jetbrains.kotlin.fir.references.FirSuperReference
import org.jetbrains.kotlin.fir.references.FirThisReference import org.jetbrains.kotlin.fir.references.FirThisReference
import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitDispatchReceiverValue import org.jetbrains.kotlin.fir.resolve.calls.ImplicitDispatchReceiverValue
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedNameError import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedNameError
@@ -462,3 +463,16 @@ private fun FirQualifiedAccess.expressionTypeOrUnitForAssignment(): ConeKotlinTy
} }
return StandardClassIds.Unit.constructClassLikeType(emptyArray(), isNullable = false) return StandardClassIds.Unit.constructClassLikeType(emptyArray(), isNullable = false)
} }
fun FirAnnotationCall.getCorrespondingConstructorReferenceOrNull(session: FirSession): FirResolvedNamedReference? =
annotationTypeRef.coneTypeSafe<ConeKotlinType>()?.classId?.let {
(session.firSymbolProvider.getClassLikeSymbolByFqName(it) as? FirRegularClassSymbol)?.fir
?.getPrimaryConstructorIfAny()
?.let { annotationConstructor ->
buildResolvedNamedReference {
source = this@getCorrespondingConstructorReferenceOrNull.source
name = it.shortClassName
resolvedSymbol = annotationConstructor.symbol
}
}
}
@@ -25,7 +25,20 @@ data class ArgumentMapping(
// parameterToCallArgumentMap.values() should be [ 'bar()', 'foo()' ] // parameterToCallArgumentMap.values() should be [ 'bar()', 'foo()' ]
val parameterToCallArgumentMap: Map<FirValueParameter, ResolvedCallArgument>, val parameterToCallArgumentMap: Map<FirValueParameter, ResolvedCallArgument>,
val diagnostics: List<ResolutionDiagnostic> val diagnostics: List<ResolutionDiagnostic>
) ) {
fun toArgumentToParameterMapping(): Map<FirExpression, FirValueParameter> {
val argumentToParameterMapping = mutableMapOf<FirExpression, FirValueParameter>()
parameterToCallArgumentMap.forEach { (valueParameter, resolvedArgument) ->
when (resolvedArgument) {
is ResolvedCallArgument.SimpleArgument -> argumentToParameterMapping[resolvedArgument.callArgument] = valueParameter
is ResolvedCallArgument.VarargArgument -> resolvedArgument.arguments.forEach {
argumentToParameterMapping[it] = valueParameter
}
}
}
return argumentToParameterMapping
}
}
private val EmptyArgumentMapping = ArgumentMapping(emptyMap(), emptyList()) private val EmptyArgumentMapping = ArgumentMapping(emptyMap(), emptyList())
@@ -159,16 +159,7 @@ internal object MapArguments : ResolutionStage() {
val function = symbol.fir val function = symbol.fir
val mapping = mapArguments(callInfo.arguments, function) val mapping = mapArguments(callInfo.arguments, function)
val argumentToParameterMapping = mutableMapOf<FirExpression, FirValueParameter>() candidate.argumentMapping = mapping.toArgumentToParameterMapping()
mapping.parameterToCallArgumentMap.forEach { (valueParameter, resolvedArgument) ->
when (resolvedArgument) {
is ResolvedCallArgument.SimpleArgument -> argumentToParameterMapping[resolvedArgument.callArgument] = valueParameter
is ResolvedCallArgument.VarargArgument -> resolvedArgument.arguments.forEach {
argumentToParameterMapping[it] = valueParameter
}
}
}
candidate.argumentMapping = argumentToParameterMapping
var applicability = CandidateApplicability.RESOLVED var applicability = CandidateApplicability.RESOLVED
mapping.diagnostics.forEach { mapping.diagnostics.forEach {
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.fir.references.impl.FirSimpleNamedReference
import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitDispatchReceiverValue import org.jetbrains.kotlin.fir.resolve.calls.ImplicitDispatchReceiverValue
import org.jetbrains.kotlin.fir.resolve.calls.candidate import org.jetbrains.kotlin.fir.resolve.calls.candidate
import org.jetbrains.kotlin.fir.resolve.calls.mapArguments
import org.jetbrains.kotlin.fir.resolve.diagnostics.* import org.jetbrains.kotlin.fir.resolve.diagnostics.*
import org.jetbrains.kotlin.fir.resolve.transformers.InvocationKindTransformer import org.jetbrains.kotlin.fir.resolve.transformers.InvocationKindTransformer
import org.jetbrains.kotlin.fir.resolve.transformers.StoreReceiver import org.jetbrains.kotlin.fir.resolve.transformers.StoreReceiver
@@ -641,6 +642,14 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
(annotationCall.transformChildren(transformer, data) as FirAnnotationCall).also { (annotationCall.transformChildren(transformer, data) as FirAnnotationCall).also {
// TODO: it's temporary incorrect solution until we design resolve and completion for annotation calls // TODO: it's temporary incorrect solution until we design resolve and completion for annotation calls
it.argumentList.transformArguments(integerLiteralTypeApproximator, null) it.argumentList.transformArguments(integerLiteralTypeApproximator, null)
if (it.arguments.any { arg -> arg is FirNamedArgumentExpression }) {
annotationCall.getCorrespondingConstructorReferenceOrNull(session)?.let { calleeReference ->
val argumentMapping =
mapArguments(it.arguments, calleeReference.resolvedSymbol.fir as FirFunction<*>)
.toArgumentToParameterMapping()
it.replaceArgumentList(buildResolvedArgumentList(argumentMapping))
}
}
it.replaceResolveStatus(status) it.replaceResolveStatus(status)
dataFlowAnalyzer.exitAnnotationCall(it) dataFlowAnalyzer.exitAnnotationCall(it)
}.compose() }.compose()
@@ -93,6 +93,9 @@ val FirClassSymbol<*>.superConeTypes
val FirClass<*>.superConeTypes get() = superTypeRefs.mapNotNull { it.coneTypeSafe<ConeClassLikeType>() } val FirClass<*>.superConeTypes get() = superTypeRefs.mapNotNull { it.coneTypeSafe<ConeClassLikeType>() }
fun FirClass<*>.getPrimaryConstructorIfAny(): FirConstructor? =
declarations.filterIsInstance<FirConstructor>().firstOrNull()?.takeIf { it.isPrimary }
fun FirRegularClass.collectEnumEntries(): Collection<FirEnumEntry> { fun FirRegularClass.collectEnumEntries(): Collection<FirEnumEntry> {
assert(classKind == ClassKind.ENUM_CLASS) assert(classKind == ClassKind.ENUM_CLASS)
return declarations.filterIsInstance<FirEnumEntry>() return declarations.filterIsInstance<FirEnumEntry>()
@@ -57,7 +57,7 @@ FILE fqName:<root> fileName:/annotationsWithDefaultParameterValues.kt
BLOCK_BODY BLOCK_BODY
FUN name:test4 visibility:public modality:FINAL <> () returnType:kotlin.Unit FUN name:test4 visibility:public modality:FINAL <> () returnType:kotlin.Unit
annotations: annotations:
A(x = '456', y = <null>) A(x = <null>, y = '456')
BLOCK_BODY BLOCK_BODY
FUN name:test5 visibility:public modality:FINAL <> () returnType:kotlin.Unit FUN name:test5 visibility:public modality:FINAL <> () returnType:kotlin.Unit
annotations: annotations:
@@ -63,7 +63,7 @@ FILE fqName:<root> fileName:/castsInsideCoroutineInference.kt
CALL 'public final fun TODO (): kotlin.Nothing [inline] declared in kotlin' type=kotlin.Nothing origin=null CALL 'public final fun TODO (): kotlin.Nothing [inline] declared in kotlin' type=kotlin.Nothing origin=null
FUN name:onCompletion visibility:public modality:FINAL <T> ($receiver:<root>.Flow<T of <root>.onCompletion>, action:kotlin.coroutines.SuspendFunction1<kotlin.Throwable?, kotlin.Unit>) returnType:IrErrorType FUN name:onCompletion visibility:public modality:FINAL <T> ($receiver:<root>.Flow<T of <root>.onCompletion>, action:kotlin.coroutines.SuspendFunction1<kotlin.Throwable?, kotlin.Unit>) returnType:IrErrorType
annotations: annotations:
Deprecated(message = GET_ENUM 'ENUM_ENTRY IR_EXTERNAL_DECLARATION_STUB name:HIDDEN' type=kotlin.DeprecationLevel, replaceWith = 'binary compatibility with a version w/o FlowCollector receiver', level = <null>) Deprecated(message = 'binary compatibility with a version w/o FlowCollector receiver', replaceWith = <null>, level = GET_ENUM 'ENUM_ENTRY IR_EXTERNAL_DECLARATION_STUB name:HIDDEN' type=kotlin.DeprecationLevel)
TYPE_PARAMETER name:T index:0 variance: superTypes:[kotlin.Any?] TYPE_PARAMETER name:T index:0 variance: superTypes:[kotlin.Any?]
$receiver: VALUE_PARAMETER name:<this> type:<root>.Flow<T of <root>.onCompletion> $receiver: VALUE_PARAMETER name:<this> type:<root>.Flow<T of <root>.onCompletion>
VALUE_PARAMETER name:action index:0 type:kotlin.coroutines.SuspendFunction1<kotlin.Throwable?, kotlin.Unit> VALUE_PARAMETER name:action index:0 type:kotlin.coroutines.SuspendFunction1<kotlin.Throwable?, kotlin.Unit>