Fix name resolution in typealias with constraint (#144)

The body of a typealias gets inlined into wherever the typealias
is used. This fixes a bug where the references in the typealias body can
resolve to the wrong value.
This commit is contained in:
Kushal Pisavadia
2024-02-15 21:28:10 +00:00
committed by GitHub
parent 1c29287344
commit 3d1db25864
11 changed files with 76 additions and 7 deletions

View File

@@ -82,7 +82,8 @@ public final class TypeAliasNode extends ExpressionNode {
simpleName,
module,
qualifiedName,
typeParameters);
typeParameters,
frame.materialize());
VmUtils.evaluateAnnotations(frame, annotationNodes, annotations);
cachedTypeAlias.initTypeCheckNode(typeAnnotationNode.execute(frame));

View File

@@ -1614,13 +1614,36 @@ public abstract class TypeNode extends PklNode {
return getMirrors(typeArgumentNodes);
}
/**
* A typealias body is effectively inlined into the type node, and not executed in its own
* frame.
*
* <p>Before executing the typealias body, use the owner and receiver of the original frame
* where the typealias was declared, so that we preserve its original scope.
*/
public void execute(VirtualFrame frame, Object value) {
var prevOwner = VmUtils.getOwner(frame);
var prevReceiver = VmUtils.getReceiver(frame);
VmUtils.setOwner(frame, VmUtils.getOwner(typeAlias.getEnclosingFrame()));
VmUtils.setReceiver(frame, VmUtils.getReceiver(typeAlias.getEnclosingFrame()));
aliasedTypeNode.execute(frame, value);
VmUtils.setOwner(frame, prevOwner);
VmUtils.setReceiver(frame, prevReceiver);
}
/** See docstring on {@link TypeAliasTypeNode#execute}. */
@Override
public void executeAndSet(VirtualFrame frame, Object value) {
var prevOwner = VmUtils.getOwner(frame);
var prevReceiver = VmUtils.getReceiver(frame);
VmUtils.setOwner(frame, VmUtils.getOwner(typeAlias.getEnclosingFrame()));
VmUtils.setReceiver(frame, VmUtils.getReceiver(typeAlias.getEnclosingFrame()));
aliasedTypeNode.executeAndSet(frame, value);
VmUtils.setOwner(frame, prevOwner);
VmUtils.setReceiver(frame, prevReceiver);
}
@Override

View File

@@ -16,6 +16,8 @@
package org.pkl.core.runtime;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.frame.Frame;
import com.oracle.truffle.api.frame.MaterializedFrame;
import com.oracle.truffle.api.source.SourceSection;
import java.util.ArrayList;
import java.util.List;
@@ -42,6 +44,7 @@ public final class VmTypeAlias extends VmValue {
private final VmTyped module;
private final String qualifiedName;
private final List<TypeParameter> typeParameters;
private final MaterializedFrame enclosingFrame;
@LateInit private TypeNode typeNode;
@@ -66,7 +69,8 @@ public final class VmTypeAlias extends VmValue {
String simpleName,
VmTyped module,
String qualifiedName,
List<TypeParameter> typeParameters) {
List<TypeParameter> typeParameters,
MaterializedFrame enclosingFrame) {
this.sourceSection = sourceSection;
this.headerSection = headerSection;
this.docComment = docComment;
@@ -76,6 +80,7 @@ public final class VmTypeAlias extends VmValue {
this.module = module;
this.qualifiedName = qualifiedName;
this.typeParameters = typeParameters;
this.enclosingFrame = enclosingFrame;
}
public void initTypeCheckNode(TypeNode typeNode) {
@@ -155,6 +160,10 @@ public final class VmTypeAlias extends VmValue {
return typeNode;
}
public Frame getEnclosingFrame() {
return enclosingFrame;
}
@TruffleBoundary
public TypeNode instantiate(TypeNode[] typeArgumentNodes) {
// Cloning the type node means that the entire type check remains within a single root node,

View File

@@ -136,6 +136,10 @@ public final class VmUtils {
return result;
}
public static void setReceiver(Frame frame, Object receiver) {
frame.getArguments()[0] = receiver;
}
public static VmObjectLike getObjectReceiver(Frame frame) {
return (VmObjectLike) getReceiver(frame);
}
@@ -156,6 +160,10 @@ public final class VmUtils {
return result;
}
public static void setOwner(Frame frame, VmObjectLike owner) {
frame.getArguments()[1] = owner;
}
/** Returns a `ObjectMember`'s key while executing the corresponding `MemberNode`. */
public static Object getMemberKey(Frame frame) {
return frame.getArguments()[2];

View File

@@ -0,0 +1,3 @@
class MyClass
typealias MyTypeAlias = MyClass(getClass() == MyClass)

View File

@@ -0,0 +1,12 @@
// length should bind to `String#length` instead of `ClassWithLengthProperty#length`
local typealias OneCharacterString = String(length == 1)
local class ClassWithLengthProperty {
length: Int = 99
// length constraint binds to ClassWithLengthProperty.length here
characters: List<OneCharacterString> = List("a", "b")
}
res1: OneCharacterString = "a"
res2 = let (c = new ClassWithLengthProperty {}) c.characters

View File

@@ -0,0 +1,8 @@
import ".../input-helper/types/typeAliasConstraint2.pkl" as helper
foo: Int = 1
bar: helper.MyTypeAlias(foo == 1) = new helper.MyClass {}
function qux(bar: helper.MyTypeAlias(foo == 1)) = (bar) {}
qux = qux(new helper.MyTypeAlias {})

View File

@@ -341,8 +341,8 @@ examples {
"abcdefg"
"abcdefg"
" abcdefg"
"Type constraint `this.length == 1` violated. Value: \"\""
"Type constraint `this.length == 1` violated. Value: \"aa\""
"Type constraint `length == 1` violated. Value: \"\""
"Type constraint `length == 1` violated. Value: \"aa\""
}
["padEnd()"] {
""
@@ -351,8 +351,8 @@ examples {
"abcdefg"
"abcdefg"
"abcdefg "
"Type constraint `this.length == 1` violated. Value: \"\""
"Type constraint `this.length == 1` violated. Value: \"aa\""
"Type constraint `length == 1` violated. Value: \"\""
"Type constraint `length == 1` violated. Value: \"aa\""
}
["toBooleanOrNull()"] {
true

View File

@@ -0,0 +1,2 @@
res1 = "a"
res2 = List("a", "b")

View File

@@ -0,0 +1,3 @@
foo = 1
bar {}
qux {}

View File

@@ -1058,7 +1058,7 @@ external class Boolean extends Any {
}
/// A Unicode character (code point).
typealias Char = String(this.length == 1) // `this.` works around rdar://75074742
typealias Char = String(length == 1)
/// A sequence of Unicode characters (code points).
///