mirror of
https://github.com/apple/pkl.git
synced 2026-01-11 22:30:54 +01:00
Execute typechecks eagerly when within a constraint (#964)
This changes the language to check all types eagerly when within a type constraint. This addresses two regressions in the language: 1. Type constraints are too relaxed (listing/mapping parameters may not be checked) 2. Failing type constraints hide members that were forced during execution of the constraint
This commit is contained in:
@@ -401,7 +401,7 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
var expr = visitExpr(exprs.get(i));
|
||||
constraints[i] = TypeConstraintNodeGen.create(expr.getSourceSection(), expr);
|
||||
}
|
||||
return new Constrained(createSourceSection(type), childNode, constraints);
|
||||
return new Constrained(createSourceSection(type), language, childNode, constraints);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -71,11 +71,30 @@ public abstract class TypeNode extends PklNode {
|
||||
*/
|
||||
public abstract TypeNode initWriteSlotNode(int slot);
|
||||
|
||||
/**
|
||||
* Checks if {@code value} conforms to this type.
|
||||
*
|
||||
* <p>Possibly returns a new object with type-casted members, in the case of {@link
|
||||
* MappingTypeNode} or {@link ListingTypeNode}.
|
||||
*
|
||||
* <p>If {@link VmLocalContext#shouldEagerTypecheck()} is true, this method will always do an
|
||||
* eager check.
|
||||
*
|
||||
* <p>If not, throws a {@link VmTypeMismatchException}.
|
||||
*/
|
||||
public final Object execute(VirtualFrame frame, Object value) {
|
||||
var localContext = VmLanguage.get(this).localContext.get();
|
||||
if (localContext.shouldEagerTypecheck()) {
|
||||
return executeEagerly(frame, value);
|
||||
}
|
||||
return executeLazily(frame, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if {@code value} conforms to this type, and possibly casts it in the case of {@link
|
||||
* MappingTypeNode} or {@link ListingTypeNode}.
|
||||
*/
|
||||
public abstract Object execute(VirtualFrame frame, Object value);
|
||||
protected abstract Object executeLazily(VirtualFrame frame, Object value);
|
||||
|
||||
/**
|
||||
* Checks if {@code value} conforms to this type, and possibly casts its value.
|
||||
@@ -92,7 +111,7 @@ public abstract class TypeNode extends PklNode {
|
||||
* check its members.
|
||||
*/
|
||||
public Object executeEagerly(VirtualFrame frame, Object value) {
|
||||
return execute(frame, value);
|
||||
return executeLazily(frame, value);
|
||||
}
|
||||
|
||||
// method arguments are used when default value contains a root node
|
||||
@@ -264,7 +283,7 @@ public abstract class TypeNode extends PklNode {
|
||||
|
||||
@Override
|
||||
public final Object executeAndSet(VirtualFrame frame, Object value) {
|
||||
var result = execute(frame, value);
|
||||
var result = executeLazily(frame, value);
|
||||
writeSlotNode.executeWithValue(frame, result);
|
||||
return result;
|
||||
}
|
||||
@@ -287,7 +306,7 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame, Object value) {
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
// do nothing
|
||||
return value;
|
||||
}
|
||||
@@ -320,14 +339,14 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame, Object value) {
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw new VmTypeMismatchException.Nothing(sourceSection, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object executeAndSet(VirtualFrame frame, Object value) {
|
||||
execute(frame, value);
|
||||
executeLazily(frame, value);
|
||||
// guaranteed to never run (execute will always throw).
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw PklBugException.unreachableCode();
|
||||
@@ -369,7 +388,7 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame, Object value) {
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
if (value instanceof VmTyped typed && typed.getVmClass() == moduleClass) return value;
|
||||
|
||||
throw typeMismatch(value, moduleClass);
|
||||
@@ -416,7 +435,7 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame, Object value) {
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
var moduleClass = ((VmTyped) getModuleNode.executeGeneric(frame)).getVmClass();
|
||||
|
||||
if (value instanceof VmTyped typed) {
|
||||
@@ -477,7 +496,7 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame, Object value) {
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
if (literal.equals(value)) return value;
|
||||
|
||||
throw typeMismatch(value, literal);
|
||||
@@ -512,7 +531,7 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame, Object value) {
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
if (value instanceof VmTyped) return value;
|
||||
|
||||
throw typeMismatch(value, BaseModule.getTypedClass());
|
||||
@@ -540,7 +559,7 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame, Object value) {
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
if (value instanceof VmDynamic) return value;
|
||||
|
||||
throw typeMismatch(value, BaseModule.getDynamicClass());
|
||||
@@ -583,7 +602,7 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame, Object value) {
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
if (value instanceof VmValue vmValue && clazz == vmValue.getVmClass()) return value;
|
||||
|
||||
throw typeMismatch(value, clazz);
|
||||
@@ -727,12 +746,12 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame, Object value) {
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
if (value instanceof VmNull) {
|
||||
// do nothing
|
||||
return value;
|
||||
}
|
||||
return elementTypeNode.execute(frame, value);
|
||||
return elementTypeNode.executeLazily(frame, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -894,7 +913,7 @@ public abstract class TypeNode extends PklNode {
|
||||
|
||||
@Fallback
|
||||
@ExplodeLoop
|
||||
public Object execute(VirtualFrame frame, Object value) {
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
if (skipElementTypeChecks) return value;
|
||||
|
||||
// escape analysis should remove this allocation in compiled code
|
||||
@@ -910,7 +929,7 @@ public abstract class TypeNode extends PklNode {
|
||||
if (shouldEagerCheck) {
|
||||
return elementTypeNode.executeEagerly(frame, value);
|
||||
} else {
|
||||
return elementTypeNode.execute(frame, value);
|
||||
return elementTypeNode.executeLazily(frame, value);
|
||||
}
|
||||
} catch (VmTypeMismatchException e) {
|
||||
typeMismatches[i] = e;
|
||||
@@ -969,7 +988,7 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame, Object value) {
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
if (contains(value)) return value;
|
||||
|
||||
throw typeMismatch(value, stringLiterals);
|
||||
@@ -1020,7 +1039,7 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame, Object value) {
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
if (value instanceof VmList vmList) {
|
||||
return evalList(frame, vmList);
|
||||
}
|
||||
@@ -1081,7 +1100,7 @@ public abstract class TypeNode extends PklNode {
|
||||
var idx = 0;
|
||||
|
||||
for (var elem : value) {
|
||||
var result = elementTypeNode.execute(frame, elem);
|
||||
var result = elementTypeNode.executeLazily(frame, elem);
|
||||
if (result != elem) {
|
||||
ret = ret.replace(idx, result);
|
||||
}
|
||||
@@ -1175,7 +1194,7 @@ public abstract class TypeNode extends PklNode {
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
@Override
|
||||
@ExplodeLoop
|
||||
public Object execute(VirtualFrame frame, Object value) {
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
if (!(value instanceof VmList vmList)) {
|
||||
throw typeMismatch(value, BaseModule.getListClass());
|
||||
}
|
||||
@@ -1184,7 +1203,7 @@ public abstract class TypeNode extends PklNode {
|
||||
var idx = 0;
|
||||
|
||||
for (var elem : vmList) {
|
||||
var result = elementTypeNode.execute(frame, elem);
|
||||
var result = elementTypeNode.executeLazily(frame, elem);
|
||||
if (result != elem) {
|
||||
ret = ret.replace(idx, result);
|
||||
}
|
||||
@@ -1296,7 +1315,7 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame, Object value) {
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
if (value instanceof VmMap vmMap) {
|
||||
return eval(frame, vmMap);
|
||||
}
|
||||
@@ -1359,7 +1378,7 @@ public abstract class TypeNode extends PklNode {
|
||||
for (var entry : value) {
|
||||
var key = VmUtils.getKey(entry);
|
||||
keyTypeNode.executeEagerly(frame, key);
|
||||
var result = valueTypeNode.execute(frame, VmUtils.getValue(entry));
|
||||
var result = valueTypeNode.executeLazily(frame, VmUtils.getValue(entry));
|
||||
if (result != VmUtils.getValue(entry)) {
|
||||
ret = ret.put(key, result);
|
||||
}
|
||||
@@ -1373,7 +1392,7 @@ public abstract class TypeNode extends PklNode {
|
||||
if (skipEntryTypeChecks) return value;
|
||||
for (var entry : value) {
|
||||
keyTypeNode.executeEagerly(frame, VmUtils.getKey(entry));
|
||||
valueTypeNode.execute(frame, VmUtils.getValue(entry));
|
||||
valueTypeNode.executeLazily(frame, VmUtils.getValue(entry));
|
||||
}
|
||||
|
||||
LoopNode.reportLoopCount(this, value.getLength());
|
||||
@@ -1393,7 +1412,7 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame, Object value) {
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
if (!(value instanceof VmListing vmListing)) {
|
||||
throw typeMismatch(value, BaseModule.getListingClass());
|
||||
}
|
||||
@@ -1459,7 +1478,7 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame, Object value) {
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
if (!(value instanceof VmMapping vmMapping)) {
|
||||
throw typeMismatch(value, BaseModule.getMappingClass());
|
||||
}
|
||||
@@ -1942,10 +1961,10 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame, Object value) {
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
if (value instanceof VmPair vmPair) {
|
||||
var first = firstTypeNode.execute(frame, vmPair.getFirst());
|
||||
var second = secondTypeNode.execute(frame, vmPair.getSecond());
|
||||
var first = firstTypeNode.executeLazily(frame, vmPair.getFirst());
|
||||
var second = secondTypeNode.executeLazily(frame, vmPair.getSecond());
|
||||
if (first == vmPair.getFirst() && second == vmPair.getSecond()) {
|
||||
return vmPair;
|
||||
}
|
||||
@@ -2026,7 +2045,7 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame, Object value) {
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw exceptionBuilder().evalError("internalStdLibClass", "VarArgs").build();
|
||||
}
|
||||
@@ -2078,7 +2097,7 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame, Object value) {
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
// do nothing
|
||||
return value;
|
||||
}
|
||||
@@ -2105,7 +2124,7 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame, Object value) {
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
if (value instanceof VmNull) {
|
||||
throw new VmTypeMismatchException.Constraint(
|
||||
BaseModule.getNonNullTypeAlias().getConstraintSection(), value);
|
||||
@@ -2146,7 +2165,7 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame, Object value) {
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
if (value instanceof Long l) {
|
||||
if ((l & mask) == l) return value;
|
||||
|
||||
@@ -2189,7 +2208,7 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame, Object value) {
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
if (value instanceof Long l) {
|
||||
if (l == l.byteValue()) return value;
|
||||
|
||||
@@ -2233,7 +2252,7 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame, Object value) {
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
if (value instanceof Long l) {
|
||||
if (l == l.shortValue()) return value;
|
||||
|
||||
@@ -2277,7 +2296,7 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame, Object value) {
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
if (value instanceof Long l) {
|
||||
if (l == l.intValue()) return value;
|
||||
|
||||
@@ -2373,21 +2392,21 @@ public abstract class TypeNode extends PklNode {
|
||||
* <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 Object execute(VirtualFrame frame, Object value) {
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
var prevOwner = VmUtils.getOwner(frame);
|
||||
var prevReceiver = VmUtils.getReceiver(frame);
|
||||
setOwner(frame, VmUtils.getOwner(typeAlias.getEnclosingFrame()));
|
||||
setReceiver(frame, VmUtils.getReceiver(typeAlias.getEnclosingFrame()));
|
||||
|
||||
try {
|
||||
return aliasedTypeNode.execute(frame, value);
|
||||
return aliasedTypeNode.executeLazily(frame, value);
|
||||
} finally {
|
||||
setOwner(frame, prevOwner);
|
||||
setReceiver(frame, prevReceiver);
|
||||
}
|
||||
}
|
||||
|
||||
/** See docstring on {@link TypeAliasTypeNode#execute}. */
|
||||
/** See docstring on {@link TypeAliasTypeNode#executeLazily}. */
|
||||
@Override
|
||||
public Object executeAndSet(VirtualFrame frame, Object value) {
|
||||
var prevOwner = VmUtils.getOwner(frame);
|
||||
@@ -2490,14 +2509,18 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
|
||||
public static final class ConstrainedTypeNode extends TypeNode {
|
||||
|
||||
private final VmLanguage language;
|
||||
@Child private TypeNode childNode;
|
||||
@Children private final TypeConstraintNode[] constraintNodes;
|
||||
|
||||
@CompilationFinal private int customThisSlot = -1;
|
||||
|
||||
public ConstrainedTypeNode(
|
||||
SourceSection sourceSection, TypeNode childNode, TypeConstraintNode[] constraintNodes) {
|
||||
SourceSection sourceSection,
|
||||
VmLanguage language,
|
||||
TypeNode childNode,
|
||||
TypeConstraintNode[] constraintNodes) {
|
||||
super(sourceSection);
|
||||
this.language = language;
|
||||
this.childNode = childNode;
|
||||
this.constraintNodes = constraintNodes;
|
||||
}
|
||||
@@ -2514,21 +2537,24 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
|
||||
@ExplodeLoop
|
||||
public Object execute(VirtualFrame frame, Object value) {
|
||||
if (customThisSlot == -1) {
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
// deferred until execution time s.t. nodes of inlined type aliases get the right frame slot
|
||||
customThisSlot =
|
||||
frame.getFrameDescriptor().findOrAddAuxiliarySlot(CustomThisScope.FRAME_SLOT_ID);
|
||||
}
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
var customThisSlot =
|
||||
frame.getFrameDescriptor().findOrAddAuxiliarySlot(CustomThisScope.FRAME_SLOT_ID);
|
||||
|
||||
var ret = childNode.execute(frame, value);
|
||||
var ret = childNode.executeLazily(frame, value);
|
||||
|
||||
var localContext = language.localContext.get();
|
||||
var prevShouldTypeCheck = localContext.shouldEagerTypecheck();
|
||||
localContext.shouldEagerTypecheck(true);
|
||||
frame.setAuxiliarySlot(customThisSlot, value);
|
||||
for (var node : constraintNodes) {
|
||||
node.execute(frame);
|
||||
try {
|
||||
for (var node : constraintNodes) {
|
||||
node.execute(frame);
|
||||
}
|
||||
return ret;
|
||||
} finally {
|
||||
localContext.shouldEagerTypecheck(prevShouldTypeCheck);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2593,7 +2619,7 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame, Object value) {
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
// do nothing
|
||||
return value;
|
||||
}
|
||||
@@ -2620,7 +2646,7 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame, Object value) {
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
if (value instanceof String) return value;
|
||||
|
||||
throw typeMismatch(value, BaseModule.getStringClass());
|
||||
@@ -2653,7 +2679,7 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame, Object value) {
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
if (value instanceof Long || value instanceof Double) return value;
|
||||
|
||||
throw typeMismatch(value, BaseModule.getNumberClass());
|
||||
@@ -2707,7 +2733,7 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame, Object value) {
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
if (value instanceof Long) return value;
|
||||
|
||||
throw typeMismatch(value, BaseModule.getIntClass());
|
||||
@@ -2740,7 +2766,7 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame, Object value) {
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
if (value instanceof Double) return value;
|
||||
|
||||
throw typeMismatch(value, BaseModule.getFloatClass());
|
||||
@@ -2748,7 +2774,7 @@ public abstract class TypeNode extends PklNode {
|
||||
|
||||
@Override
|
||||
public Object executeAndSet(VirtualFrame frame, Object value) {
|
||||
execute(frame, value);
|
||||
executeLazily(frame, value);
|
||||
frame.setDouble(slot, (double) value);
|
||||
return value;
|
||||
}
|
||||
@@ -2780,7 +2806,7 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame, Object value) {
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
if (value instanceof Boolean) return value;
|
||||
|
||||
throw typeMismatch(value, BaseModule.getBooleanClass());
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -36,14 +36,18 @@ public abstract class UnresolvedTypeNode extends PklNode {
|
||||
public abstract TypeNode execute(VirtualFrame frame);
|
||||
|
||||
public static final class Constrained extends UnresolvedTypeNode {
|
||||
|
||||
private final VmLanguage language;
|
||||
@Child UnresolvedTypeNode childNode;
|
||||
TypeConstraintNode[] constraintCheckNodes;
|
||||
|
||||
public Constrained(
|
||||
SourceSection sourceSection,
|
||||
VmLanguage language,
|
||||
UnresolvedTypeNode childNode,
|
||||
TypeConstraintNode[] constraintCheckNodes) {
|
||||
super(sourceSection);
|
||||
this.language = language;
|
||||
this.childNode = childNode;
|
||||
this.constraintCheckNodes = constraintCheckNodes;
|
||||
}
|
||||
@@ -52,7 +56,8 @@ public abstract class UnresolvedTypeNode extends PklNode {
|
||||
public TypeNode execute(VirtualFrame frame) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
|
||||
return new ConstrainedTypeNode(sourceSection, childNode.execute(frame), constraintCheckNodes);
|
||||
return new ConstrainedTypeNode(
|
||||
sourceSection, language, childNode.execute(frame), constraintCheckNodes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.pkl.core.runtime;
|
||||
|
||||
import com.oracle.truffle.api.CallTarget;
|
||||
import com.oracle.truffle.api.ContextThreadLocal;
|
||||
import com.oracle.truffle.api.TruffleLanguage;
|
||||
import com.oracle.truffle.api.TruffleLanguage.ContextPolicy;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
@@ -45,6 +46,9 @@ public final class VmLanguage extends TruffleLanguage<VmContext> {
|
||||
return REFERENCE.get(node);
|
||||
}
|
||||
|
||||
public final ContextThreadLocal<VmLocalContext> localContext =
|
||||
locals.createContextThreadLocal((ignoredCtx, ignoredThread) -> new VmLocalContext());
|
||||
|
||||
@Override
|
||||
protected VmContext createContext(Env env) {
|
||||
return new VmContext();
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright © 2025 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.runtime;
|
||||
|
||||
/** A per-context thread-local value that can be used to influence execution. */
|
||||
public class VmLocalContext {
|
||||
private boolean shouldEagerTypecheck = false;
|
||||
|
||||
public VmLocalContext() {}
|
||||
|
||||
public void shouldEagerTypecheck(boolean shouldEagerTypecheck) {
|
||||
this.shouldEagerTypecheck = shouldEagerTypecheck;
|
||||
}
|
||||
|
||||
public boolean shouldEagerTypecheck() {
|
||||
return this.shouldEagerTypecheck;
|
||||
}
|
||||
}
|
||||
9
pkl-core/src/test/files/LanguageSnippetTests/input-helper/classes/MyClass.pkl
vendored
Normal file
9
pkl-core/src/test/files/LanguageSnippetTests/input-helper/classes/MyClass.pkl
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
typealias EmailAddress = String(matches(Regex(#".+@\S+|.+<\S+@\S+>"#)))
|
||||
|
||||
class MyClass {
|
||||
emails: Listing<EmailAddress>
|
||||
}
|
||||
|
||||
myClass: MyClass
|
||||
|
||||
others: Listing<module(this != module)>
|
||||
7
pkl-core/src/test/files/LanguageSnippetTests/input-helper/classes/myClass1.pkl
vendored
Normal file
7
pkl-core/src/test/files/LanguageSnippetTests/input-helper/classes/myClass1.pkl
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
amends ".../input-helper/classes/MyClass.pkl"
|
||||
|
||||
myClass {
|
||||
emails {
|
||||
"baz@bar.com"
|
||||
}
|
||||
}
|
||||
16
pkl-core/src/test/files/LanguageSnippetTests/input/classes/constraints13.pkl
vendored
Normal file
16
pkl-core/src/test/files/LanguageSnippetTests/input/classes/constraints13.pkl
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
import "pkl:test"
|
||||
|
||||
const local isOddLengthOfBirds = (it: Listing<Bird>) -> it.length.isOdd
|
||||
|
||||
class Bird
|
||||
|
||||
class MyTest {
|
||||
// function parameter type should be checked eagerly
|
||||
birds: Listing(isOddLengthOfBirds) = new {
|
||||
1
|
||||
2
|
||||
3
|
||||
}
|
||||
}
|
||||
|
||||
res = test.catch(() -> new MyTest {}.birds)
|
||||
14
pkl-core/src/test/files/LanguageSnippetTests/input/classes/constraints14.pkl
vendored
Normal file
14
pkl-core/src/test/files/LanguageSnippetTests/input/classes/constraints14.pkl
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
// This test executes constraint `EmailAddress` within `MyClass` from two different root nodes:
|
||||
// - ListingOrMappingTypeCastNode
|
||||
// - PropertyTypeNode
|
||||
amends ".../input-helper/classes/MyClass.pkl"
|
||||
|
||||
myClass {
|
||||
emails {
|
||||
"foo@bar.com"
|
||||
}
|
||||
}
|
||||
|
||||
others {
|
||||
import(".../input-helper/classes/myClass1.pkl")
|
||||
}
|
||||
9
pkl-core/src/test/files/LanguageSnippetTests/input/errors/constraintDetails1.pkl
vendored
Normal file
9
pkl-core/src/test/files/LanguageSnippetTests/input/errors/constraintDetails1.pkl
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
// Error message should include `new Bird { name = "Bob" }`
|
||||
birds: Listing(firstOneIsSandy) = new {
|
||||
new Bird { name = "Bob" }
|
||||
new Bird { name = "Bob" }
|
||||
}
|
||||
|
||||
hidden firstOneIsSandy = (it: Listing<Bird>) -> it[0].name == "Sandy"
|
||||
|
||||
class Bird { name: String }
|
||||
11
pkl-core/src/test/files/LanguageSnippetTests/input/errors/constraintDetails2.pkl
vendored
Normal file
11
pkl-core/src/test/files/LanguageSnippetTests/input/errors/constraintDetails2.pkl
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
// Error message should include `new Bird { name = "Bob" }`
|
||||
birds: Listing(
|
||||
let (myself: Listing<Bird> = this)
|
||||
myself[0].name == "Sandy"
|
||||
) =
|
||||
new {
|
||||
new Bird { name = "Bob" }
|
||||
new Bird { name = "Bob" }
|
||||
}
|
||||
|
||||
class Bird { name: String }
|
||||
9
pkl-core/src/test/files/LanguageSnippetTests/input/errors/constraintDetails3.pkl
vendored
Normal file
9
pkl-core/src/test/files/LanguageSnippetTests/input/errors/constraintDetails3.pkl
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
// typechecks within child frames should also be eagerly checked
|
||||
foo: Listing(toList().every((it: Listing<Bird>) -> it[0].name == "Bob")) = new {
|
||||
new Listing {
|
||||
new Bird { name = "Eagle" }
|
||||
new Bird { name = "Quail" }
|
||||
}
|
||||
}
|
||||
|
||||
class Bird { name: String }
|
||||
1
pkl-core/src/test/files/LanguageSnippetTests/output/classes/constraints13.pcf
vendored
Normal file
1
pkl-core/src/test/files/LanguageSnippetTests/output/classes/constraints13.pcf
vendored
Normal file
@@ -0,0 +1 @@
|
||||
res = "Expected value of type `constraints13#Bird`, but got type `Int`. Value: 1"
|
||||
15
pkl-core/src/test/files/LanguageSnippetTests/output/classes/constraints14.pcf
vendored
Normal file
15
pkl-core/src/test/files/LanguageSnippetTests/output/classes/constraints14.pcf
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
myClass {
|
||||
emails {
|
||||
"foo@bar.com"
|
||||
}
|
||||
}
|
||||
others {
|
||||
new {
|
||||
myClass {
|
||||
emails {
|
||||
"baz@bar.com"
|
||||
}
|
||||
}
|
||||
others {}
|
||||
}
|
||||
}
|
||||
15
pkl-core/src/test/files/LanguageSnippetTests/output/errors/constraintDetails1.err
vendored
Normal file
15
pkl-core/src/test/files/LanguageSnippetTests/output/errors/constraintDetails1.err
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `firstOneIsSandy` violated.
|
||||
Value: new Listing { new Bird { name = "Bob" }; new Bird { name = ? } }
|
||||
|
||||
x | birds: Listing(firstOneIsSandy) = new {
|
||||
^^^^^^^^^^^^^^^
|
||||
at constraintDetails1#birds (file:///$snippetsDir/input/errors/constraintDetails1.pkl)
|
||||
|
||||
x | birds: Listing(firstOneIsSandy) = new {
|
||||
^^^^^
|
||||
at constraintDetails1#birds (file:///$snippetsDir/input/errors/constraintDetails1.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
16
pkl-core/src/test/files/LanguageSnippetTests/output/errors/constraintDetails2.err
vendored
Normal file
16
pkl-core/src/test/files/LanguageSnippetTests/output/errors/constraintDetails2.err
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `let (myself: Listing<Bird> = this)
|
||||
myself[0].name == "Sandy"` violated.
|
||||
Value: new Listing { new Bird { name = "Bob" }; new Bird { name = ? } }
|
||||
|
||||
x | let (myself: Listing<Bird> = this)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at constraintDetails2#birds (file:///$snippetsDir/input/errors/constraintDetails2.pkl)
|
||||
|
||||
x | new {
|
||||
^^^^^
|
||||
at constraintDetails2#birds (file:///$snippetsDir/input/errors/constraintDetails2.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
15
pkl-core/src/test/files/LanguageSnippetTests/output/errors/constraintDetails3.err
vendored
Normal file
15
pkl-core/src/test/files/LanguageSnippetTests/output/errors/constraintDetails3.err
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `toList().every((it: Listing<Bird>) -> it[0].name == "Bob")` violated.
|
||||
Value: new Listing { new Listing { new Bird { name = "Eagle" }; new Bird { name = ? ...
|
||||
|
||||
x | foo: Listing(toList().every((it: Listing<Bird>) -> it[0].name == "Bob")) = new {
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at constraintDetails3#foo (file:///$snippetsDir/input/errors/constraintDetails3.pkl)
|
||||
|
||||
x | foo: Listing(toList().every((it: Listing<Bird>) -> it[0].name == "Bob")) = new {
|
||||
^^^^^
|
||||
at constraintDetails3#foo (file:///$snippetsDir/input/errors/constraintDetails3.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
Reference in New Issue
Block a user