From 39fee12f32711f78c895b321bcf95e6a7212ac64 Mon Sep 17 00:00:00 2001 From: Kristoffer Andersen Date: Tue, 23 Nov 2021 17:56:14 +0100 Subject: [PATCH] [IR/FE] BindingContext: Change `USED_AS_EXPRESSION` to Basic Slice This commit changes the slice implementation used for `USED_AS_EXPRESSION`. Here is the problem as identified and mitigated by this commit: Slices are key-value maps. They are not total over the domain of keys, so "looking up" a key not in the slice is a valid operation, yielding `null`. A binding context is a collection of slices. Binding contexts can be stacked as composite binding contexts to e.g. facilitate scoped analyses. Looking up a key proceeds down the stack, yielding the first non-null value, or `null` if no slice in the stack of binding contexts contain that key. A slice made by `createSimpleSetSlice` models a "set" of keys that can be enlarged by inclusion: adding `(k, true)` to the slice indicates `k` belongs to the set. `(k, false)` indicates non-membership. However, looking up a key _not in_ the slice yields _false_, rather than null. Hence, simple set slices do not compose in composite binding traces. This was encountered porting the expression evaluator to the IR backend. PSI2IR uses `USED_AS_EXPRESSION` to generate expression body functions properly. The frontend analysis of the fragment is layered ontop of the binding context from the editor's analysis of the underlying project being debugged, which contains speficially the analysis results of inline functions called from the fragment. --- .../kotlin/resolve/BindingContext.java | 3 ++- .../bindingContextUtil/BindingContextUtils.kt | 6 +----- .../kotlin/util/slicedMap/Slices.java | 18 ++++++++++++++++++ .../generators/BranchingExpressionGenerator.kt | 3 ++- 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java index 5d88ede8299..a3251f5159c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java @@ -51,6 +51,7 @@ import java.util.Collections; import static org.jetbrains.kotlin.util.slicedMap.RewritePolicy.DO_NOTHING; import static org.jetbrains.kotlin.util.slicedMap.Slices.COMPILE_TIME_VALUE_REWRITE_POLICY; +import static org.jetbrains.kotlin.util.slicedMap.Slices.CONSERVATIVE_SET_INCLUSION_SEMANTICS; public interface BindingContext { BindingContext EMPTY = new BindingContext() { @@ -179,7 +180,7 @@ public interface BindingContext { WritableSlice PROCESSED = Slices.createSimpleSlice(); // Please do not use this slice (USED_AS_EXPRESSION) directly, // use extension element.isUsedAsExpression() instead - WritableSlice USED_AS_EXPRESSION = Slices.createSimpleSetSlice(); + WritableSlice USED_AS_EXPRESSION = new BasicWritableSlice<>(CONSERVATIVE_SET_INCLUSION_SEMANTICS); WritableSlice USED_AS_RESULT_OF_LAMBDA = Slices.createSimpleSetSlice(); WritableSlice CAPTURED_IN_CLOSURE = new BasicWritableSlice<>(DO_NOTHING); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/bindingContextUtil/BindingContextUtils.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/bindingContextUtil/BindingContextUtils.kt index 6a24b78e4f0..b3e15cbbbaf 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/bindingContextUtil/BindingContextUtils.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/bindingContextUtil/BindingContextUtils.kt @@ -57,11 +57,7 @@ fun KtReturnExpression.getTargetFunction(context: BindingContext): KtCallableDec } fun KtExpression.isUsedAsExpression(context: BindingContext): Boolean = - context[USED_AS_EXPRESSION, this] - ?: throw AssertionError( - "BindingContext returned null for Boolean slice: " + - if (context == EMPTY) "BindingContext.EMPTY" else context.javaClass.toString() - ) + context[USED_AS_EXPRESSION, this] ?: false fun KtExpression.isUsedAsResultOfLambda(context: BindingContext): Boolean = context[USED_AS_RESULT_OF_LAMBDA, this]!! fun KtExpression.isUsedAsStatement(context: BindingContext): Boolean = !isUsedAsExpression(context) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/Slices.java b/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/Slices.java index 494434aef08..4b7d8b99120 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/Slices.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/Slices.java @@ -80,6 +80,24 @@ public class Slices { } }; + // For Boolean-valued slices modelling set-membership, this policy + // implements constructing a set by element-wise inclusion, conservatively: + // allow rewriting false->true but not true->false. + // + // Used in growing the set of `USED_AS_EXPRESSION` in CFG analysis. + public static final RewritePolicy CONSERVATIVE_SET_INCLUSION_SEMANTICS = new RewritePolicy() { + @Override + public boolean rewriteProcessingNeeded(K key) { + return true; + } + + @Override + public boolean processRewrite(WritableSlice slice, K key, V oldValue, V newValue) { + assert (newValue instanceof Boolean); + return (Boolean)newValue; + } + }; + private Slices() { } diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/BranchingExpressionGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/BranchingExpressionGenerator.kt index da3dbbde25c..ab11928b8fa 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/BranchingExpressionGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/BranchingExpressionGenerator.kt @@ -32,6 +32,7 @@ import org.jetbrains.kotlin.psi.psiUtil.startOffsetSkippingComments import org.jetbrains.kotlin.psi2ir.deparenthesize import org.jetbrains.kotlin.psi2ir.intermediate.loadAt import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression import org.jetbrains.kotlin.utils.SmartList class BranchingExpressionGenerator(statementGenerator: StatementGenerator) : StatementGeneratorExtension(statementGenerator) { @@ -142,7 +143,7 @@ class BranchingExpressionGenerator(statementGenerator: StatementGenerator) : Sta } private fun addElseBranchForExhaustiveWhenIfNeeded(irWhen: IrWhen, whenExpression: KtWhenExpression) { - val isUsedAsExpression = true == get(BindingContext.USED_AS_EXPRESSION, whenExpression) + val isUsedAsExpression = whenExpression.isUsedAsExpression(context.bindingContext) val isImplicitElseRequired = if (isUsedAsExpression) true == get(BindingContext.EXHAUSTIVE_WHEN, whenExpression)