[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.
This commit is contained in:
committed by
Alexander Udalov
parent
a70c336c2c
commit
39fee12f32
@@ -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<KtExpression, Boolean> PROCESSED = Slices.createSimpleSlice();
|
||||
// Please do not use this slice (USED_AS_EXPRESSION) directly,
|
||||
// use extension element.isUsedAsExpression() instead
|
||||
WritableSlice<KtElement, Boolean> USED_AS_EXPRESSION = Slices.createSimpleSetSlice();
|
||||
WritableSlice<KtElement, Boolean> USED_AS_EXPRESSION = new BasicWritableSlice<>(CONSERVATIVE_SET_INCLUSION_SEMANTICS);
|
||||
WritableSlice<KtElement, Boolean> USED_AS_RESULT_OF_LAMBDA = Slices.createSimpleSetSlice();
|
||||
|
||||
WritableSlice<VariableDescriptor, CaptureKind> CAPTURED_IN_CLOSURE = new BasicWritableSlice<>(DO_NOTHING);
|
||||
|
||||
+1
-5
@@ -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)
|
||||
|
||||
@@ -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 <K> boolean rewriteProcessingNeeded(K key) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <K, V> boolean processRewrite(WritableSlice<K, V> slice, K key, V oldValue, V newValue) {
|
||||
assert (newValue instanceof Boolean);
|
||||
return (Boolean)newValue;
|
||||
}
|
||||
};
|
||||
|
||||
private Slices() {
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user