Fix name resolution of types in constraints (#1826)

This fixes an issue where type names are resolved incorrectly
in typealias constraints.

Co-authored-by: Islon Scherer <islonscherer@gmail.com>
This commit is contained in:
Daniel Chao
2026-08-19 11:10:32 -07:00
committed by GitHub
co-authored by Islon Scherer
parent ac43bdbb17
commit 4dd37219c0
12 changed files with 129 additions and 16 deletions
@@ -129,6 +129,7 @@ import org.pkl.core.ast.expression.primary.ExecuteCustomThisWithRootNode;
import org.pkl.core.ast.expression.primary.GetEnclosingReceiverNode; import org.pkl.core.ast.expression.primary.GetEnclosingReceiverNode;
import org.pkl.core.ast.expression.primary.GetMemberKeyNode; import org.pkl.core.ast.expression.primary.GetMemberKeyNode;
import org.pkl.core.ast.expression.primary.GetModuleNode; import org.pkl.core.ast.expression.primary.GetModuleNode;
import org.pkl.core.ast.expression.primary.GetModuleOwnerNode;
import org.pkl.core.ast.expression.primary.GetOwnerNode; import org.pkl.core.ast.expression.primary.GetOwnerNode;
import org.pkl.core.ast.expression.primary.GetReceiverNode; import org.pkl.core.ast.expression.primary.GetReceiverNode;
import org.pkl.core.ast.expression.primary.GetTypeAliasModuleNode; import org.pkl.core.ast.expression.primary.GetTypeAliasModuleNode;
@@ -2225,13 +2226,21 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
private ResolveDeclaredTypeNode doVisitTypeName(QualifiedIdentifier ctx) { private ResolveDeclaredTypeNode doVisitTypeName(QualifiedIdentifier ctx) {
var identifiers = ctx.getIdentifiers(); var identifiers = ctx.getIdentifiers();
var getModuleNode =
isBaseModule
? new ConstantValueNode(BaseModule.getModule())
: symbolTable.isInTypeAliasScope
? new GetTypeAliasModuleNode(VmUtils.unavailableSourceSection())
: new GetModuleOwnerNode(VmUtils.unavailableSourceSection());
return switch (identifiers.size()) { return switch (identifiers.size()) {
case 1 -> { case 1 -> {
var identifier = identifiers.get(0); var identifier = identifiers.get(0);
var sourceSection = createSourceSection(identifier);
yield new ResolveSimpleDeclaredTypeNode( yield new ResolveSimpleDeclaredTypeNode(
createSourceSection(identifier), sourceSection,
org.pkl.core.runtime.Identifier.get(identifier.getValue()), org.pkl.core.runtime.Identifier.get(identifier.getValue()),
isBaseModule); isBaseModule,
getModuleNode);
} }
case 2 -> { case 2 -> {
var identifier1 = identifiers.get(0); var identifier1 = identifiers.get(0);
@@ -2241,7 +2250,8 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
createSourceSection(identifier1), createSourceSection(identifier1),
createSourceSection(identifier2), createSourceSection(identifier2),
org.pkl.core.runtime.Identifier.localProperty(identifier1.getValue()), org.pkl.core.runtime.Identifier.localProperty(identifier1.getValue()),
org.pkl.core.runtime.Identifier.get(identifier2.getValue())); org.pkl.core.runtime.Identifier.get(identifier2.getValue()),
getModuleNode);
} }
default -> default ->
throw exceptionBuilder() throw exceptionBuilder()
@@ -41,6 +41,10 @@ public final class ExecuteCustomThisWithRootNode extends ExpressionNode {
new CustomThisNode(VmUtils.unavailableSourceSection()); new CustomThisNode(VmUtils.unavailableSourceSection());
private @Child DirectCallNode callNode; private @Child DirectCallNode callNode;
// shouldn't be marked `@Child` because this node is actually the child of the SimpleRootNode
// created in the constructor.
private final ExpressionNode expressionNode;
public ExecuteCustomThisWithRootNode( public ExecuteCustomThisWithRootNode(
SourceSection sourceSection, SourceSection sourceSection,
ExpressionNode expressionNode, ExpressionNode expressionNode,
@@ -49,6 +53,7 @@ public final class ExecuteCustomThisWithRootNode extends ExpressionNode {
int[] forGeneratorSlots, int[] forGeneratorSlots,
int[] parameterSlots) { int[] parameterSlots) {
super(sourceSection); super(sourceSection);
this.expressionNode = expressionNode;
frameDescriptor.findOrAddAuxiliarySlot(CustomThisScope.FRAME_SLOT_ID); frameDescriptor.findOrAddAuxiliarySlot(CustomThisScope.FRAME_SLOT_ID);
var rootNode = var rootNode =
new SimpleRootNode( new SimpleRootNode(
@@ -60,6 +65,10 @@ public final class ExecuteCustomThisWithRootNode extends ExpressionNode {
this.callNode = DirectCallNode.create(rootNode.getCallTarget()); this.callNode = DirectCallNode.create(rootNode.getCallTarget());
} }
public ExpressionNode getExpressionNode() {
return expressionNode;
}
@Override @Override
public Object executeGeneric(VirtualFrame frame) { public Object executeGeneric(VirtualFrame frame) {
var customThis = customThisNode.executeGeneric(frame); var customThis = customThisNode.executeGeneric(frame);
@@ -0,0 +1,46 @@
/*
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.ast.expression.primary;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection;
import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.runtime.VmUtils;
public final class GetModuleOwnerNode extends ExpressionNode {
public GetModuleOwnerNode(SourceSection sourceSection) {
super(sourceSection);
}
@Override
public Object executeGeneric(VirtualFrame frame) {
CompilerDirectives.transferToInterpreter();
var levelsUp = -1;
for (var current = VmUtils.getOwner(frame);
current != null;
current = current.getEnclosingOwner()) {
if (!current.isParseTimeInvisibleScope()) {
levelsUp += 1;
}
}
return replace(levelsUp == 0 ? new GetOwnerNode() : new GetEnclosingOwnerNode(levelsUp))
.executeGeneric(frame);
}
}
@@ -18,12 +18,14 @@ package org.pkl.core.ast.member;
import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;
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;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import org.pkl.core.TypeParameter; import org.pkl.core.TypeParameter;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.expression.primary.ExecuteCustomThisWithRootNode;
import org.pkl.core.ast.expression.primary.GetTypeAliasModuleNode; import org.pkl.core.ast.expression.primary.GetTypeAliasModuleNode;
import org.pkl.core.ast.type.UnresolvedTypeNode; import org.pkl.core.ast.type.UnresolvedTypeNode;
import org.pkl.core.runtime.VmTypeAlias; import org.pkl.core.runtime.VmTypeAlias;
@@ -87,16 +89,26 @@ public final class TypeAliasNode extends ExpressionNode {
frame.materialize()); frame.materialize());
VmUtils.evaluateAnnotations(frame, annotationNodes, annotations); VmUtils.evaluateAnnotations(frame, annotationNodes, annotations);
initTypeAliasModule(typeAnnotationNode, module);
var bodyTypeNode = typeAnnotationNode.execute(frame); var bodyTypeNode = typeAnnotationNode.execute(frame);
bodyTypeNode.accept(
node -> {
if (node instanceof GetTypeAliasModuleNode getTypeAliasModuleNode) {
getTypeAliasModuleNode.lateInitModule(module);
}
return true;
});
cachedTypeAlias.initTypeCheckNode(bodyTypeNode); cachedTypeAlias.initTypeCheckNode(bodyTypeNode);
return cachedTypeAlias; return cachedTypeAlias;
} }
private static void initTypeAliasModule(Node node, VmTyped module) {
node.accept(
n -> {
if (n instanceof GetTypeAliasModuleNode getTypeAliasModuleNode) {
getTypeAliasModuleNode.lateInitModule(module);
// `node.accept` won't walk past root nodes, but this can happen if we have a
// let expression in a constraint.
//
// If we encounter `ExecuteCustomThisWithRootNode`; walk its expression node directly.
} else if (n instanceof ExecuteCustomThisWithRootNode executeCustomThisWithRootNode) {
initTypeAliasModule(executeCustomThisWithRootNode.getExpressionNode(), module);
}
return true;
});
}
} }
@@ -1,5 +1,5 @@
/* /*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. * Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@ package org.pkl.core.ast.type;
import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.runtime.*; import org.pkl.core.runtime.*;
public final class ResolveQualifiedDeclaredTypeNode extends ResolveDeclaredTypeNode { public final class ResolveQualifiedDeclaredTypeNode extends ResolveDeclaredTypeNode {
@@ -25,19 +26,22 @@ public final class ResolveQualifiedDeclaredTypeNode extends ResolveDeclaredTypeN
private final SourceSection typeNameSection; private final SourceSection typeNameSection;
private final Identifier moduleName; private final Identifier moduleName;
private final Identifier typeName; private final Identifier typeName;
@Child private ExpressionNode getModuleNode;
public ResolveQualifiedDeclaredTypeNode( public ResolveQualifiedDeclaredTypeNode(
SourceSection sourceSection, SourceSection sourceSection,
SourceSection moduleNameSection, SourceSection moduleNameSection,
SourceSection typeNameSection, SourceSection typeNameSection,
Identifier moduleName, Identifier moduleName,
Identifier typeName) { Identifier typeName,
ExpressionNode getModuleNode) {
super(sourceSection); super(sourceSection);
this.moduleNameSection = moduleNameSection; this.moduleNameSection = moduleNameSection;
this.typeNameSection = typeNameSection; this.typeNameSection = typeNameSection;
this.moduleName = moduleName; this.moduleName = moduleName;
this.typeName = typeName; this.typeName = typeName;
this.getModuleNode = getModuleNode;
assert moduleName.isLocalProp(); assert moduleName.isLocalProp();
assert typeName.isRegular(); assert typeName.isRegular();
@@ -47,7 +51,7 @@ public final class ResolveQualifiedDeclaredTypeNode extends ResolveDeclaredTypeN
public Object executeGeneric(VirtualFrame frame) { public Object executeGeneric(VirtualFrame frame) {
CompilerDirectives.transferToInterpreter(); CompilerDirectives.transferToInterpreter();
var enclosingModule = getEnclosingModule(VmUtils.getOwner(frame)); var enclosingModule = (VmTyped) getModuleNode.executeGeneric(frame);
var importedModule = getImport(enclosingModule, moduleName, moduleNameSection); var importedModule = getImport(enclosingModule, moduleName, moduleNameSection);
// search module hierarchy // search module hierarchy
@@ -1,5 +1,5 @@
/* /*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. * Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -18,18 +18,24 @@ package org.pkl.core.ast.type;
import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.runtime.*; import org.pkl.core.runtime.*;
public final class ResolveSimpleDeclaredTypeNode extends ResolveDeclaredTypeNode { public final class ResolveSimpleDeclaredTypeNode extends ResolveDeclaredTypeNode {
private final Identifier typeName; private final Identifier typeName;
private final boolean isBaseModule; private final boolean isBaseModule;
@Child private ExpressionNode getModuleNode;
public ResolveSimpleDeclaredTypeNode( public ResolveSimpleDeclaredTypeNode(
SourceSection sourceSection, Identifier typeName, boolean isBaseModule) { SourceSection sourceSection,
Identifier typeName,
boolean isBaseModule,
ExpressionNode getModuleNode) {
super(sourceSection); super(sourceSection);
this.typeName = typeName; this.typeName = typeName;
this.isBaseModule = isBaseModule; this.isBaseModule = isBaseModule;
this.getModuleNode = getModuleNode;
} }
@Override @Override
@@ -37,7 +43,7 @@ public final class ResolveSimpleDeclaredTypeNode extends ResolveDeclaredTypeNode
CompilerDirectives.transferToInterpreter(); CompilerDirectives.transferToInterpreter();
var localTypeName = typeName.toLocalProperty(); var localTypeName = typeName.toLocalProperty();
var enclosingModule = getEnclosingModule(VmUtils.getOwner(frame)); var enclosingModule = (VmTyped) getModuleNode.executeGeneric(frame);
// search enclosing module for local class/type alias or module import // search enclosing module for local class/type alias or module import
var result = getType(enclosingModule, localTypeName, sourceSection); var result = getType(enclosingModule, localTypeName, sourceSection);
@@ -1,3 +1,8 @@
class MyClass class MyClass
typealias MyTypeAlias = MyClass(getClass() == MyClass) typealias MyTypeAlias = MyClass(getClass() == MyClass)
typealias MyTypeAlias2 = MyClass(this is MyClass)
// a let expression turns the constraint node into its own root node
typealias MyTypeAlias3 = MyClass(let (_ = true) this is MyClass)
@@ -0,0 +1,3 @@
import "typeAliasConstraint2.pkl"
typealias MyTypeAlias = Any(this is typeAliasConstraint2.MyClass)
@@ -0,0 +1,8 @@
import "../../input-helper/types/typeAliasConstraint2.pkl"
import "../../input-helper/types/typeAliasConstraint3.pkl"
res: typeAliasConstraint2.MyTypeAlias2 = new typeAliasConstraint2.MyClass {}
res2: typeAliasConstraint3.MyTypeAlias = new typeAliasConstraint2.MyClass {}
res3: typeAliasConstraint2.MyTypeAlias3 = new typeAliasConstraint2.MyClass {}
@@ -0,0 +1,6 @@
import "../../input-helper/types/typeAliasAmendModule.pkl"
local const function isValid(s: String): Boolean = s.startsWith("Alice")
res = typeAliasAmendModule.name
@@ -0,0 +1,3 @@
res {}
res2 {}
res3 {}
@@ -0,0 +1 @@
res = "Bob Marley"