Lookup for local variables taking into account uninitialized this
Consider a context with uninitialized this, e.g.:
fun foo() {
val x = "..."
class Local(y: String) : Base(L@{ x + y })
}
Lambda 'L' is an argument of a super class constructor call.
Here 'this@Local' is not initialized yet. Thus local variables captured
in 'Local' can't be used. Instead, they should be captured by lambda 'L'
itself.
Note that lambda 'L' sees both 'x' and 'y' as local variables that
should be captured.
When in context with uninitialized this (generating arguments for super
type constructor or delegating constructor call), and a variable in
question is not found in the current context, use enclosing local lookup
to determine whether a local variable should be captured by a closure.
This commit is contained in:
@@ -393,6 +393,9 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
new ImplementationBodyCodegen(declaration, objectContext, classBuilder, state, getParentCodegen(), /* isLocal = */ true).generate();
|
||||
|
||||
if (declaration instanceof KtClass && ((KtClass) declaration).isInterface()) {
|
||||
// TODO consider dropping this code
|
||||
// It looks like this code generates DefaultImpl class for a local interface.
|
||||
// Local interfaces are prohibited in Kotlin 1.0.
|
||||
Type traitImplType = state.getTypeMapper().mapDefaultImpls(descriptor);
|
||||
ClassBuilder traitImplBuilder = state.getFactory().newVisitor(JvmDeclarationOriginKt.DefaultImpls(declaration, descriptor), traitImplType, declaration.getContainingFile());
|
||||
ClassContext traitImplContext = context.intoAnonymousClass(descriptor, this, OwnerKind.DEFAULT_IMPLS);
|
||||
@@ -1869,7 +1872,16 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
|
||||
@Override
|
||||
public boolean isLocal(DeclarationDescriptor descriptor) {
|
||||
return lookupLocalIndex(descriptor) != -1;
|
||||
if (lookupLocalIndex(descriptor) != -1) return true;
|
||||
|
||||
if (context.isContextWithUninitializedThis()) {
|
||||
LocalLookup outerLookup = context.getParentContext().getEnclosingLocalLookup();
|
||||
if (outerLookup != null) {
|
||||
return outerLookup.isLocal(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public int lookupLocalIndex(DeclarationDescriptor descriptor) {
|
||||
|
||||
@@ -354,26 +354,28 @@ public abstract class CodegenContext<T extends DeclarationDescriptor> {
|
||||
|
||||
@Nullable
|
||||
public CodegenContext getEnclosingClassContext() {
|
||||
CodegenContext cur = getParentContext();
|
||||
CodegenContext cur = getEnclosingThisContext();
|
||||
while (cur != null) {
|
||||
if (cur.isContextWithUninitializedThis()) {
|
||||
// If the current context is a constructor with uninitialized 'this',
|
||||
// skip it and the corresponding class context
|
||||
CodegenContext parent = cur.getParentContext();
|
||||
assert parent != null : "Context " + cur + " should have a parent";
|
||||
cur = parent;
|
||||
}
|
||||
else {
|
||||
DeclarationDescriptor curDescriptor = cur.getContextDescriptor();
|
||||
if (curDescriptor instanceof ClassDescriptor) {
|
||||
return cur;
|
||||
}
|
||||
DeclarationDescriptor curDescriptor = cur.getContextDescriptor();
|
||||
if (curDescriptor instanceof ClassDescriptor) {
|
||||
return cur;
|
||||
}
|
||||
cur = cur.getParentContext();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public CodegenContext getEnclosingThisContext() {
|
||||
CodegenContext cur = getParentContext();
|
||||
while (cur != null && cur.isContextWithUninitializedThis()) {
|
||||
CodegenContext parent = cur.getParentContext();
|
||||
assert parent != null : "Context " + cur + " should have a parent";
|
||||
cur = parent.getParentContext();
|
||||
}
|
||||
return cur;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ClassDescriptor getEnclosingClass() {
|
||||
// TODO store enclosing context class in the context itself
|
||||
@@ -541,8 +543,10 @@ public abstract class CodegenContext<T extends DeclarationDescriptor> {
|
||||
StackValue resultValue;
|
||||
if (myOuter != null && getEnclosingClass() == d) {
|
||||
resultValue = result;
|
||||
} else {
|
||||
resultValue = parentContext != null ? parentContext.lookupInContext(d, result, state, ignoreNoOuter) : null;
|
||||
}
|
||||
else {
|
||||
CodegenContext enclosingClassContext = getEnclosingThisContext();
|
||||
resultValue = enclosingClassContext != null ? enclosingClassContext.lookupInContext(d, result, state, ignoreNoOuter) : null;
|
||||
}
|
||||
|
||||
if (myOuter != null && resultValue != null && !isStaticField(resultValue)) {
|
||||
@@ -709,4 +713,9 @@ public abstract class CodegenContext<T extends DeclarationDescriptor> {
|
||||
public CodegenContext getFirstCrossInlineOrNonInlineContext() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public LocalLookup getEnclosingLocalLookup() {
|
||||
return enclosingLocalLookup;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user