mirror of
https://github.com/apple/pkl.git
synced 2026-04-24 17:28:37 +02: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,
|
simpleName,
|
||||||
module,
|
module,
|
||||||
qualifiedName,
|
qualifiedName,
|
||||||
typeParameters);
|
typeParameters,
|
||||||
|
frame.materialize());
|
||||||
|
|
||||||
VmUtils.evaluateAnnotations(frame, annotationNodes, annotations);
|
VmUtils.evaluateAnnotations(frame, annotationNodes, annotations);
|
||||||
cachedTypeAlias.initTypeCheckNode(typeAnnotationNode.execute(frame));
|
cachedTypeAlias.initTypeCheckNode(typeAnnotationNode.execute(frame));
|
||||||
|
|||||||
@@ -1614,13 +1614,36 @@ public abstract class TypeNode extends PklNode {
|
|||||||
return getMirrors(typeArgumentNodes);
|
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) {
|
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);
|
aliasedTypeNode.execute(frame, value);
|
||||||
|
VmUtils.setOwner(frame, prevOwner);
|
||||||
|
VmUtils.setReceiver(frame, prevReceiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** See docstring on {@link TypeAliasTypeNode#execute}. */
|
||||||
@Override
|
@Override
|
||||||
public void executeAndSet(VirtualFrame frame, Object value) {
|
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);
|
aliasedTypeNode.executeAndSet(frame, value);
|
||||||
|
|
||||||
|
VmUtils.setOwner(frame, prevOwner);
|
||||||
|
VmUtils.setReceiver(frame, prevReceiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
package org.pkl.core.runtime;
|
package org.pkl.core.runtime;
|
||||||
|
|
||||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
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 com.oracle.truffle.api.source.SourceSection;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -42,6 +44,7 @@ public final class VmTypeAlias extends VmValue {
|
|||||||
private final VmTyped module;
|
private final VmTyped module;
|
||||||
private final String qualifiedName;
|
private final String qualifiedName;
|
||||||
private final List<TypeParameter> typeParameters;
|
private final List<TypeParameter> typeParameters;
|
||||||
|
private final MaterializedFrame enclosingFrame;
|
||||||
|
|
||||||
@LateInit private TypeNode typeNode;
|
@LateInit private TypeNode typeNode;
|
||||||
|
|
||||||
@@ -66,7 +69,8 @@ public final class VmTypeAlias extends VmValue {
|
|||||||
String simpleName,
|
String simpleName,
|
||||||
VmTyped module,
|
VmTyped module,
|
||||||
String qualifiedName,
|
String qualifiedName,
|
||||||
List<TypeParameter> typeParameters) {
|
List<TypeParameter> typeParameters,
|
||||||
|
MaterializedFrame enclosingFrame) {
|
||||||
this.sourceSection = sourceSection;
|
this.sourceSection = sourceSection;
|
||||||
this.headerSection = headerSection;
|
this.headerSection = headerSection;
|
||||||
this.docComment = docComment;
|
this.docComment = docComment;
|
||||||
@@ -76,6 +80,7 @@ public final class VmTypeAlias extends VmValue {
|
|||||||
this.module = module;
|
this.module = module;
|
||||||
this.qualifiedName = qualifiedName;
|
this.qualifiedName = qualifiedName;
|
||||||
this.typeParameters = typeParameters;
|
this.typeParameters = typeParameters;
|
||||||
|
this.enclosingFrame = enclosingFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initTypeCheckNode(TypeNode typeNode) {
|
public void initTypeCheckNode(TypeNode typeNode) {
|
||||||
@@ -155,6 +160,10 @@ public final class VmTypeAlias extends VmValue {
|
|||||||
return typeNode;
|
return typeNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Frame getEnclosingFrame() {
|
||||||
|
return enclosingFrame;
|
||||||
|
}
|
||||||
|
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
public TypeNode instantiate(TypeNode[] typeArgumentNodes) {
|
public TypeNode instantiate(TypeNode[] typeArgumentNodes) {
|
||||||
// Cloning the type node means that the entire type check remains within a single root node,
|
// 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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void setReceiver(Frame frame, Object receiver) {
|
||||||
|
frame.getArguments()[0] = receiver;
|
||||||
|
}
|
||||||
|
|
||||||
public static VmObjectLike getObjectReceiver(Frame frame) {
|
public static VmObjectLike getObjectReceiver(Frame frame) {
|
||||||
return (VmObjectLike) getReceiver(frame);
|
return (VmObjectLike) getReceiver(frame);
|
||||||
}
|
}
|
||||||
@@ -156,6 +160,10 @@ public final class VmUtils {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void setOwner(Frame frame, VmObjectLike owner) {
|
||||||
|
frame.getArguments()[1] = owner;
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns a `ObjectMember`'s key while executing the corresponding `MemberNode`. */
|
/** Returns a `ObjectMember`'s key while executing the corresponding `MemberNode`. */
|
||||||
public static Object getMemberKey(Frame frame) {
|
public static Object getMemberKey(Frame frame) {
|
||||||
return frame.getArguments()[2];
|
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"
|
"abcdefg"
|
||||||
" abcdefg"
|
" abcdefg"
|
||||||
"Type constraint `this.length == 1` violated. Value: \"\""
|
"Type constraint `length == 1` violated. Value: \"\""
|
||||||
"Type constraint `this.length == 1` violated. Value: \"aa\""
|
"Type constraint `length == 1` violated. Value: \"aa\""
|
||||||
}
|
}
|
||||||
["padEnd()"] {
|
["padEnd()"] {
|
||||||
""
|
""
|
||||||
@@ -351,8 +351,8 @@ examples {
|
|||||||
"abcdefg"
|
"abcdefg"
|
||||||
"abcdefg"
|
"abcdefg"
|
||||||
"abcdefg "
|
"abcdefg "
|
||||||
"Type constraint `this.length == 1` violated. Value: \"\""
|
"Type constraint `length == 1` violated. Value: \"\""
|
||||||
"Type constraint `this.length == 1` violated. Value: \"aa\""
|
"Type constraint `length == 1` violated. Value: \"aa\""
|
||||||
}
|
}
|
||||||
["toBooleanOrNull()"] {
|
["toBooleanOrNull()"] {
|
||||||
true
|
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).
|
/// 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).
|
/// A sequence of Unicode characters (code points).
|
||||||
///
|
///
|
||||||
|
|||||||
Reference in New Issue
Block a user