mirror of
https://github.com/apple/pkl.git
synced 2026-03-23 17:41:10 +01:00
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:
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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];
|
||||
|
||||
3
pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typeAliasConstraint2.pkl
vendored
Normal file
3
pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typeAliasConstraint2.pkl
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
class MyClass
|
||||
|
||||
typealias MyTypeAlias = MyClass(getClass() == MyClass)
|
||||
12
pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAliasConstraint1.pkl
vendored
Normal file
12
pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAliasConstraint1.pkl
vendored
Normal 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
|
||||
8
pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAliasConstraint2.pkl
vendored
Normal file
8
pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAliasConstraint2.pkl
vendored
Normal 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 {})
|
||||
@@ -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
|
||||
|
||||
2
pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAliasConstraint1.pcf
vendored
Normal file
2
pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAliasConstraint1.pcf
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
res1 = "a"
|
||||
res2 = List("a", "b")
|
||||
3
pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAliasConstraint2.pcf
vendored
Normal file
3
pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAliasConstraint2.pcf
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
foo = 1
|
||||
bar {}
|
||||
qux {}
|
||||
@@ -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).
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user