Skip to content

Commit

Permalink
feature: improve type handling for MethodHandles#tryFinally() (#136)
Browse files Browse the repository at this point in the history
  • Loading branch information
SirYwell authored Nov 8, 2024
1 parent 45d0a7b commit 593c5bc
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ class MethodHandlesMerger(private val ssaAnalyzer: SsaAnalyzer) : ProblemEmitter
if (handlerParameterTypes is CompleteTypeLatticeElementList && (handlerParameterTypes.size == 0
|| (exType is ExactType && !handlerParameterTypes[0].canBe(exType.psiType)))
) {
emitProblem<MethodHandleType>(handlerExpr, message("problem.merging.catchException.missingException", exType))
emitProblem<MethodHandleType>(
handlerExpr,
message("problem.merging.catchException.missingException", exType)
)
}
val returnType = if (target.returnType != handler.returnType) {
emitIncompatibleReturnTypes(targetExpr, target.returnType, handler.returnType)
Expand Down Expand Up @@ -317,7 +320,8 @@ class MethodHandlesMerger(private val ssaAnalyzer: SsaAnalyzer) : ProblemEmitter
val combinerSignature = combiner // (A...)V
if (targetSignature.parameterTypes.sizeMatches { pos >= it } == TriState.YES) return topType
val combinerIsVoid = combinerSignature.returnType.match(PsiTypes.voidType())
val combinerParameterList = combinerSignature.parameterTypes as? CompleteTypeLatticeElementList ?: return topType
val combinerParameterList =
combinerSignature.parameterTypes as? CompleteTypeLatticeElementList ?: return topType
val sub: List<Type> = when (combinerIsVoid) {
TriState.YES -> combinerParameterList.typeList
TriState.NO -> listOf(combinerSignature.returnType) + combinerParameterList.typeList
Expand Down Expand Up @@ -480,20 +484,38 @@ class MethodHandlesMerger(private val ssaAnalyzer: SsaAnalyzer) : ProblemEmitter
cleanupExpr: PsiExpression,
block: SsaConstruction.Block
): MethodHandleType {
val target = ssaAnalyzer.methodHandleType(targetExpr, block) ?: bottomType
val cleanup = ssaAnalyzer.methodHandleType(cleanupExpr, block) ?: bottomType
if (target is BotMethodHandleType || cleanup is BotMethodHandleType) return bottomType
// TODO should use join
if (cleanup.returnType != target.returnType) return topType
val target = ssaAnalyzer.methodHandleType(targetExpr, block) ?: topType
val cleanup = ssaAnalyzer.methodHandleType(cleanupExpr, block) ?: topType
val (newReturn, identical) = cleanup.returnType.joinIdentical(target.returnType)
if (identical == TriState.NO) {
emitProblem<MethodHandleType>(
cleanupExpr,
message("problem.merging.tryFinally.returnTypeMismatch", target.returnType, cleanup.returnType)
)
}
val isVoid = target.returnType.match(PsiTypes.voidType())
val leading = when (isVoid) {
TriState.YES -> 1
TriState.NO -> 2
TriState.UNKNOWN -> return complete(target.returnType, TopTypeList)
TriState.UNKNOWN -> return complete(newReturn, TopTypeList)
}
if (cleanup.parameterTypes.sizeMatches { it < leading } == TriState.YES) {
emitProblem<MethodHandleType>(
cleanupExpr,
message(
"problem.merging.tryFinally.missingCleanupParameters",
leading,
cleanup.parameterTypes.sizeOrNull() ?: 0
)
)
}
if (cleanup.parameterTypes.sizeMatches { it < leading } == TriState.YES) return topType
// TODO 0th param must be <= Throwable
if (isVoid == TriState.NO && cleanup.parameterTypes[1] != cleanup.returnType) return topType
if (isVoid == TriState.NO && cleanup.parameterTypes[1].joinIdentical(newReturn).second == TriState.NO) {
emitProblem<MethodHandleType>(
cleanupExpr,
message("problem.merging.tryFinally.secondParameter", cleanup.parameterTypes[1], newReturn)
)
}
val aList = cleanup.parameterTypes.dropFirst(leading) as? CompleteTypeLatticeElementList ?: return topType
val targetParameterList = target.parameterTypes as? CompleteTypeLatticeElementList ?: return topType
if (!targetParameterList.typeList.startsWith(aList.typeList)) return topType
Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/messages/MethodHandleMessages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,6 @@ problem.foreign.memory.dereferenceElementInvalid=Address layout has no target la
problem.foreign.memory.dereferenceElementNotAllowed=Dereference path element is not allowed here.
problem.transforming.asType.redundant=Call to 'asType' is redundant as the MethodHandle already has that type
problem.transforming.asType.incompatibleToPrimitiveCast=Cannot cast reference type {0} to primitive type {1}
problem.merging.tryFinally.returnTypeMismatch=Return types do not match: {0} != {1}
problem.merging.tryFinally.missingCleanupParameters=Expected at least {0} parameters, got {1}
problem.merging.tryFinally.secondParameter=Parameter at index 1 must match the return type ({1}) but was {0}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ class MethodHandleInspectionsTest : TypeAnalysisTestBase() {

fun testMethodHandlesThrowException() = doTypeCheckingTest()

fun testMethodHandlesTryFinally() = doInspectionAndTypeCheckingTest()

fun testMethodHandlesZero() = doInspectionTest()

fun testMethodHandleAsType() = doInspectionAndTypeCheckingTest()
Expand Down
23 changes: 23 additions & 0 deletions src/test/testData/MethodHandlesTryFinally.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;

import static java.lang.invoke.MethodType.methodType;

class MethodHandlesTryFinally {
void tryFinally_() {
<info descr="()int">MethodHandle mh00 = <info descr="()int">MethodHandles.zero(int.class)</info>;</info>
<info descr="(Exception,int)double">MethodHandle mh01 = <info descr="(Exception,int)double">MethodHandles.empty(<info descr="(Exception,int)double">methodType(double.class, Exception.class, int.class)</info>)</info>;</info>
<info descr="(Exception)int">MethodHandle mh02 = <info descr="(Exception)int">MethodHandles.empty(<info descr="(Exception)int">methodType(int.class, Exception.class)</info>)</info>;</info>
<info descr="(Exception,double)int">MethodHandle mh03 = <info descr="(Exception,double)int">MethodHandles.empty(<info descr="(Exception,double)int">methodType(int.class, Exception.class, double.class)</info>)</info>;</info>
<info descr="(Exception,int)int">MethodHandle mh04 = <info descr="(Exception,int)int">MethodHandles.empty(<info descr="(Exception,int)int">methodType(int.class, Exception.class, int.class)</info>)</info>;</info>

// incompatible return types
<info descr="()int">MethodHandle mh10 = <info descr="()int">MethodHandles.tryFinally(mh00, <warning descr="Return types do not match: int != double">mh01</warning>)</info>;</info>
// missing parameter at index 1
<info descr="⊤">MethodHandle mh11 = <info descr="⊤">MethodHandles.tryFinally(mh00, <warning descr="Expected at least 2 parameters, got 1">mh02</warning>)</info>;</info>
// parameter at index 1 type mismatch
<info descr="()int">MethodHandle mh12 = <info descr="()int">MethodHandles.tryFinally(mh00, <warning descr="Parameter at index 1 must match the return type (int) but was double">mh03</warning>)</info>;</info>
// no type issues!
<info descr="()int">MethodHandle mh13 = <info descr="()int">MethodHandles.tryFinally(mh00, mh04)</info>;</info>
}
}

0 comments on commit 593c5bc

Please sign in to comment.