Fix parameterized type checks in alias constraints (#1906)
Fixes an issue where a constraint inside a typealias that writes to frame slots is unable to resolve type arguments.
This commit is contained in:
1 parent
08f0c3781b
commit
ce977f1ffe
4 files changed
+67
-18
No files matched your search
+21
@@ -18,6 +18,7 @@ package org.pkl.core.ast.expression.primary;
|
||||
import com.oracle.truffle.api.frame.FrameDescriptor;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.DirectCallNode;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.SimpleRootNode;
|
||||
@@ -43,6 +44,10 @@ public final class ExecuteCustomThisWithRootNode extends ExpressionNode {
|
||||
// shouldn't be marked `@Child` because this node is actually the child of the SimpleRootNode
|
||||
// created in the constructor.
|
||||
private final ExpressionNode expressionNode;
|
||||
private final FrameDescriptor frameDescriptor;
|
||||
private final String qualifiedName;
|
||||
private final int[] forGeneratorSlots;
|
||||
private final int[] parameterSlots;
|
||||
|
||||
public ExecuteCustomThisWithRootNode(
|
||||
SourceSection sourceSection,
|
||||
@@ -53,6 +58,10 @@ public final class ExecuteCustomThisWithRootNode extends ExpressionNode {
|
||||
int[] parameterSlots) {
|
||||
super(sourceSection);
|
||||
this.expressionNode = expressionNode;
|
||||
this.frameDescriptor = frameDescriptor;
|
||||
this.qualifiedName = qualifiedName;
|
||||
this.forGeneratorSlots = forGeneratorSlots;
|
||||
this.parameterSlots = parameterSlots;
|
||||
frameDescriptor.findOrAddAuxiliarySlot(VmUtils.CUSTOM_THIS_FRAME_SLOT_ID);
|
||||
var rootNode =
|
||||
new SimpleRootNode(
|
||||
@@ -68,6 +77,18 @@ public final class ExecuteCustomThisWithRootNode extends ExpressionNode {
|
||||
return expressionNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node deepCopy() {
|
||||
// The expression belongs to a separate root and is not copied as a child of this node.
|
||||
return new ExecuteCustomThisWithRootNode(
|
||||
sourceSection,
|
||||
(ExpressionNode) expressionNode.deepCopy(),
|
||||
frameDescriptor,
|
||||
qualifiedName,
|
||||
forGeneratorSlots,
|
||||
parameterSlots);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object executeGeneric(VirtualFrame frame) {
|
||||
var customThis = customThisNode.executeGeneric(frame);
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.google.errorprone.annotations.concurrent.GuardedBy;
|
||||
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.nodes.NodeVisitor;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
@@ -30,6 +31,7 @@ import org.pkl.core.PObject;
|
||||
import org.pkl.core.TypeAlias;
|
||||
import org.pkl.core.TypeParameter;
|
||||
import org.pkl.core.ast.VmModifier;
|
||||
import org.pkl.core.ast.expression.primary.ExecuteCustomThisWithRootNode;
|
||||
import org.pkl.core.ast.type.TypeNode;
|
||||
import org.pkl.core.ast.type.TypeNode.ConstrainedTypeNode;
|
||||
import org.pkl.core.ast.type.TypeNode.TypeVariableNode;
|
||||
@@ -200,25 +202,31 @@ public final class VmTypeAlias extends VmValue {
|
||||
|
||||
var clone = (TypeNode) typeNode.deepCopy();
|
||||
clone.accept(
|
||||
node -> {
|
||||
if (node instanceof TypeVariableNode typeVarNode) {
|
||||
var index = typeVarNode.getTypeParameterIndex();
|
||||
node.replace(
|
||||
typeArgumentNodes.length == 0
|
||||
? new UnknownTypeNode(sourceSection)
|
||||
: deepCopy(typeArgumentNodes[index]));
|
||||
} else if (node instanceof UnresolvedTypeNode.TypeVariable unresolvedTypeVar) {
|
||||
// Type variables inside constraint expressions (e.g. `every((it) -> it is T)`)
|
||||
// are still unresolved at instantiation time. Replace them with a resolved
|
||||
// unresolved type node that returns the concrete type argument.
|
||||
var index = unresolvedTypeVar.getTypeParameterIndex();
|
||||
node.replace(
|
||||
typeArgumentNodes.length == 0
|
||||
? new UnresolvedTypeNode.Unknown(sourceSection)
|
||||
: new UnresolvedTypeNode.Resolved(
|
||||
sourceSection, deepCopy(typeArgumentNodes[index])));
|
||||
new NodeVisitor() {
|
||||
@Override
|
||||
public boolean visit(com.oracle.truffle.api.nodes.Node node) {
|
||||
if (node instanceof TypeVariableNode typeVarNode) {
|
||||
var index = typeVarNode.getTypeParameterIndex();
|
||||
node.replace(
|
||||
typeArgumentNodes.length == 0
|
||||
? new UnknownTypeNode(sourceSection)
|
||||
: deepCopy(typeArgumentNodes[index]));
|
||||
} else if (node instanceof UnresolvedTypeNode.TypeVariable unresolvedTypeVar) {
|
||||
// Type variables inside constraint expressions (e.g. `every((it) -> it is T)`)
|
||||
// are still unresolved at instantiation time. Replace them with a resolved
|
||||
// unresolved type node that returns the concrete type argument.
|
||||
var index = unresolvedTypeVar.getTypeParameterIndex();
|
||||
node.replace(
|
||||
typeArgumentNodes.length == 0
|
||||
? new UnresolvedTypeNode.Unknown(sourceSection)
|
||||
: new UnresolvedTypeNode.Resolved(
|
||||
sourceSection, deepCopy(typeArgumentNodes[index])));
|
||||
} else if (node instanceof ExecuteCustomThisWithRootNode customThisNode) {
|
||||
// A constraint with frame slots runs in a separate root, outside this tree.
|
||||
customThisNode.getExpressionNode().accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
return clone;
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import "pkl:test"
|
||||
|
||||
class Foo { bar: Int }
|
||||
const function isOk(f: Foo) = f.bar > 0
|
||||
|
||||
typealias WithInferredNew<X> = Int(isOk(new { bar = 1 }) && this is X)
|
||||
typealias WithLet<X> = Int((let (n = 1) n == 1) && this is X)
|
||||
|
||||
wrongType = 3 is WithInferredNew<String>
|
||||
matchingType = 3 is WithInferredNew<Int>
|
||||
wrongTypeAgain = 3 is WithInferredNew<String>
|
||||
letWrongType = 3 is WithLet<String>
|
||||
letMatchingType = 3 is WithLet<Int>
|
||||
castWrongType = test.catch(() -> 3 as WithInferredNew<String>)
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
wrongType = false
|
||||
matchingType = true
|
||||
wrongTypeAgain = false
|
||||
letWrongType = false
|
||||
letMatchingType = true
|
||||
castWrongType = "Type constraint `isOk(new { bar = 1 }) && this is X` violated. Value: 3"
|
||||
Reference in new issue
Block a user