mirror of
https://github.com/apple/pkl.git
synced 2026-04-23 00:38:37 +02:00
Typecheck Mapping/Listing members lazily (#628)
This changes how the language performs typechecks for mappings and listings. Currently, Pkl will shallow-force any Mapping and Listing to check it the type parameter (e.g. Listing<Person> means each element is checked to be an instance of Person). This changes the language to check each member's type when the member is accessed. This also adjust test runner to handle thrown errors from within tests. With the change to make mapping/listing typechecks lazy, we can now correctly handle thrown errors from within a single test case. This adjusts the test runner to consider any thrown errors as a failure for that specific test case.
This commit is contained in:
@@ -22,7 +22,6 @@ import java.util.function.Function;
|
||||
import org.pkl.core.ast.member.DefaultPropertyBodyNode;
|
||||
import org.pkl.core.runtime.VmExceptionBuilder;
|
||||
import org.pkl.core.runtime.VmLanguage;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
import org.pkl.core.util.Nullable;
|
||||
|
||||
public abstract class MemberNode extends PklRootNode {
|
||||
@@ -57,19 +56,6 @@ public abstract class MemberNode extends PklRootNode {
|
||||
return new VmExceptionBuilder().withSourceSection(getHeaderSection());
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, the property value computed by this node is not the final value exposed to user code
|
||||
* but will still be amended.
|
||||
*
|
||||
* <p>Used to disable type check for to-be-amended properties. See {@link
|
||||
* org.pkl.core.runtime.VmUtils#SKIP_TYPECHECK_MARKER}. IDEA: might be more appropriate to only
|
||||
* skip constraints check
|
||||
*/
|
||||
protected final boolean shouldRunTypeCheck(VirtualFrame frame) {
|
||||
return frame.getArguments().length != 4
|
||||
|| frame.getArguments()[3] != VmUtils.SKIP_TYPECHECK_MARKER;
|
||||
}
|
||||
|
||||
public boolean isUndefined() {
|
||||
return bodyNode instanceof DefaultPropertyBodyNode propBodyNode && propBodyNode.isUndefined();
|
||||
}
|
||||
|
||||
@@ -442,13 +442,21 @@ public final class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
: doVisitNewExprWithInferredParent(ctx);
|
||||
}
|
||||
|
||||
// `new Listing<Person> {}` is sugar for: `new Listing<Person> {} as Listing<Person>`
|
||||
private Object doVisitNewExprWithExplicitParent(NewExprContext ctx, TypeContext typeCtx) {
|
||||
return doVisitObjectBody(
|
||||
ctx.objectBody(),
|
||||
new GetParentForTypeNode(
|
||||
createSourceSection(ctx),
|
||||
visitType(typeCtx),
|
||||
symbolTable.getCurrentScope().getQualifiedName()));
|
||||
var parentType = visitType(typeCtx);
|
||||
var expr =
|
||||
doVisitObjectBody(
|
||||
ctx.objectBody(),
|
||||
new GetParentForTypeNode(
|
||||
createSourceSection(ctx),
|
||||
parentType,
|
||||
symbolTable.getCurrentScope().getQualifiedName()));
|
||||
if (typeCtx instanceof DeclaredTypeContext declaredTypeContext
|
||||
&& declaredTypeContext.typeArgumentList() != null) {
|
||||
return new TypeCastNode(parentType.getSourceSection(), expr, parentType);
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
private Object doVisitNewExprWithInferredParent(NewExprContext ctx) {
|
||||
|
||||
@@ -103,7 +103,7 @@ public abstract class GeneratorPredicateMemberNode extends GeneratorMemberNode {
|
||||
var callTarget = member.getCallTarget();
|
||||
value = callTarget.call(parent, owner, key);
|
||||
}
|
||||
owner.setCachedValue(key, value);
|
||||
owner.setCachedValue(key, value, member);
|
||||
}
|
||||
|
||||
frame.setAuxiliarySlot(customThisSlot, value);
|
||||
|
||||
@@ -150,8 +150,8 @@ public abstract class EntriesLiteralNode extends SpecializedObjectLiteralNode {
|
||||
|
||||
@Fallback
|
||||
@TruffleBoundary
|
||||
protected void fallback(Object parent) {
|
||||
elementsEntriesFallback(parent, values[0], false);
|
||||
protected Object fallback(Object parent) {
|
||||
return elementsEntriesFallback(parent, values[0], false);
|
||||
}
|
||||
|
||||
@ExplodeLoop
|
||||
|
||||
@@ -285,7 +285,7 @@ public abstract class SpecializedObjectLiteralNode extends ObjectLiteralNode {
|
||||
}
|
||||
|
||||
@TruffleBoundary
|
||||
protected void elementsEntriesFallback(
|
||||
protected Object elementsEntriesFallback(
|
||||
Object parent, @Nullable ObjectMember firstElemOrEntry, boolean isElementsOnly) {
|
||||
var parentIsClass = parent instanceof VmClass;
|
||||
var parentClass = parentIsClass ? (VmClass) parent : VmUtils.getClass(parent);
|
||||
|
||||
@@ -71,7 +71,7 @@ public final class ReadLocalPropertyNode extends ExpressionNode {
|
||||
|
||||
if (result == null) {
|
||||
result = callNode.call(objReceiver, owner, property.getName());
|
||||
objReceiver.setCachedValue(property, result);
|
||||
objReceiver.setCachedValue(property, result, property);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -184,12 +184,12 @@ public final class ResolveVariableNode extends ExpressionNode {
|
||||
if (member != null) {
|
||||
var constantValue = member.getConstantValue();
|
||||
if (constantValue != null) {
|
||||
baseModule.setCachedValue(variableName, constantValue);
|
||||
baseModule.setCachedValue(variableName, constantValue, member);
|
||||
return new ConstantValueNode(sourceSection, constantValue);
|
||||
}
|
||||
|
||||
var computedValue = member.getCallTarget().call(baseModule, baseModule);
|
||||
baseModule.setCachedValue(variableName, computedValue);
|
||||
baseModule.setCachedValue(variableName, computedValue, member);
|
||||
return new ConstantValueNode(sourceSection, computedValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ public final class FunctionNode extends RegularMemberNode {
|
||||
var result = bodyNode.executeGeneric(frame);
|
||||
|
||||
if (checkedReturnTypeNode != null) {
|
||||
checkedReturnTypeNode.execute(frame, result);
|
||||
return checkedReturnTypeNode.execute(frame, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright © 2024 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.member;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.frame.FrameDescriptor;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.pkl.core.ast.PklRootNode;
|
||||
import org.pkl.core.ast.type.TypeNode;
|
||||
import org.pkl.core.ast.type.VmTypeMismatchException;
|
||||
import org.pkl.core.runtime.VmLanguage;
|
||||
import org.pkl.core.util.Nullable;
|
||||
|
||||
/** Performs a typecast on a Mapping entry value, or a Listing element. */
|
||||
public class ListingOrMappingTypeCastNode extends PklRootNode {
|
||||
|
||||
@Child private TypeNode typeNode;
|
||||
private final String qualifiedName;
|
||||
|
||||
public ListingOrMappingTypeCastNode(
|
||||
VmLanguage language, FrameDescriptor descriptor, TypeNode typeNode, String qualifiedName) {
|
||||
super(language, descriptor);
|
||||
this.typeNode = typeNode;
|
||||
this.qualifiedName = qualifiedName;
|
||||
}
|
||||
|
||||
public TypeNode getTypeNode() {
|
||||
return typeNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SourceSection getSourceSection() {
|
||||
return typeNode.getSourceSection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getName() {
|
||||
return qualifiedName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame) {
|
||||
try {
|
||||
return typeNode.execute(frame, frame.getArguments()[2]);
|
||||
} catch (VmTypeMismatchException e) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw e.toVmException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,8 +66,7 @@ public final class LocalTypedPropertyNode extends RegularMemberNode {
|
||||
unresolvedTypeNode = null;
|
||||
}
|
||||
var result = bodyNode.executeGeneric(frame);
|
||||
typeNode.execute(frame, result);
|
||||
return result;
|
||||
return typeNode.execute(frame, result);
|
||||
} catch (VmTypeMismatchException e) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw e.toVmException();
|
||||
|
||||
@@ -134,7 +134,8 @@ public final class ObjectMember extends Member {
|
||||
var skip = 0;
|
||||
var text = candidate.getCharacters();
|
||||
var ch = text.charAt(skip);
|
||||
while (ch == '=' || Character.isWhitespace(ch)) {
|
||||
// body section of entries needs to chomp the ending delimiter too.
|
||||
while ((ch == ']' && isEntry()) || ch == '=' || Character.isWhitespace(ch)) {
|
||||
ch = text.charAt(++skip);
|
||||
}
|
||||
return source.createSection(candidate.getCharIndex() + skip, candidate.getCharLength() - skip);
|
||||
|
||||
@@ -62,10 +62,9 @@ public final class PropertyTypeNode extends PklRootNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Object execute(VirtualFrame frame) {
|
||||
public Object execute(VirtualFrame frame) {
|
||||
try {
|
||||
typeNode.execute(frame, frame.getArguments()[2]);
|
||||
return null;
|
||||
return typeNode.execute(frame, frame.getArguments()[2]);
|
||||
} catch (VmTypeMismatchException e) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw e.toVmException();
|
||||
|
||||
@@ -54,8 +54,8 @@ public abstract class TypeCheckedPropertyNode extends RegularMemberNode {
|
||||
var result = executeBody(frame);
|
||||
|
||||
// TODO: propagate SUPER_CALL_MARKER to disable constraint (but not type) check
|
||||
if (callNode != null && shouldRunTypeCheck(frame)) {
|
||||
callNode.call(VmUtils.getReceiverOrNull(frame), property.getOwner(), result);
|
||||
if (callNode != null && VmUtils.shouldRunTypeCheck(frame)) {
|
||||
return callNode.call(VmUtils.getReceiverOrNull(frame), property.getOwner(), result);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -67,11 +67,11 @@ public abstract class TypeCheckedPropertyNode extends RegularMemberNode {
|
||||
|
||||
var result = executeBody(frame);
|
||||
|
||||
if (shouldRunTypeCheck(frame)) {
|
||||
if (VmUtils.shouldRunTypeCheck(frame)) {
|
||||
var property = getProperty(owner.getVmClass());
|
||||
var typeAnnNode = property.getTypeNode();
|
||||
if (typeAnnNode != null) {
|
||||
callNode.call(
|
||||
return callNode.call(
|
||||
typeAnnNode.getCallTarget(),
|
||||
VmUtils.getReceiverOrNull(frame),
|
||||
property.getOwner(),
|
||||
|
||||
@@ -45,8 +45,9 @@ public final class TypedPropertyNode extends RegularMemberNode {
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame) {
|
||||
var propertyValue = executeBody(frame);
|
||||
if (shouldRunTypeCheck(frame)) {
|
||||
typeCheckCallNode.call(VmUtils.getReceiver(frame), VmUtils.getOwner(frame), propertyValue);
|
||||
if (VmUtils.shouldRunTypeCheck(frame)) {
|
||||
return typeCheckCallNode.call(
|
||||
VmUtils.getReceiver(frame), VmUtils.getOwner(frame), propertyValue);
|
||||
}
|
||||
return propertyValue;
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ public final class IdentityMixinNode extends PklRootNode {
|
||||
try {
|
||||
var argument = arguments[2];
|
||||
if (argumentTypeNode != null) {
|
||||
argumentTypeNode.execute(frame, argument);
|
||||
return argumentTypeNode.execute(frame, argument);
|
||||
}
|
||||
return argument;
|
||||
} catch (VmTypeMismatchException e) {
|
||||
|
||||
@@ -73,7 +73,7 @@ public abstract class ResolveDeclaredTypeNode extends ExpressionNode {
|
||||
var result = module.getCachedValue(importName);
|
||||
if (result == null) {
|
||||
result = callNode.call(member.getCallTarget(), module, module, importName);
|
||||
module.setCachedValue(importName, result);
|
||||
module.setCachedValue(importName, result, member);
|
||||
}
|
||||
return (VmTyped) result;
|
||||
}
|
||||
@@ -94,7 +94,7 @@ public abstract class ResolveDeclaredTypeNode extends ExpressionNode {
|
||||
var result = module.getCachedValue(typeName);
|
||||
if (result == null) {
|
||||
result = callNode.call(member.getCallTarget(), module, module, typeName);
|
||||
module.setCachedValue(typeName, result);
|
||||
module.setCachedValue(typeName, result, member);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -17,10 +17,12 @@ package org.pkl.core.ast.type;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.util.LateInit;
|
||||
|
||||
@NodeInfo(shortName = "as")
|
||||
public final class TypeCastNode extends ExpressionNode {
|
||||
@Child private ExpressionNode valueNode;
|
||||
@Child private UnresolvedTypeNode unresolvedTypeNode;
|
||||
@@ -47,8 +49,7 @@ public final class TypeCastNode extends ExpressionNode {
|
||||
|
||||
var value = valueNode.executeGeneric(frame);
|
||||
try {
|
||||
typeNode.execute(frame, value);
|
||||
return value;
|
||||
return typeNode.execute(frame, value);
|
||||
} catch (VmTypeMismatchException e) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw e.toVmException();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,10 +17,12 @@ package org.pkl.core.ast.type;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.util.Nullable;
|
||||
|
||||
@NodeInfo(shortName = "is")
|
||||
public final class TypeTestNode extends ExpressionNode {
|
||||
@Child private ExpressionNode valueNode;
|
||||
@Child private UnresolvedTypeNode unresolvedTypeNode;
|
||||
@@ -50,9 +52,11 @@ public final class TypeTestNode extends ExpressionNode {
|
||||
unresolvedTypeNode = null;
|
||||
}
|
||||
|
||||
// TODO: throw if typeNode is FunctionTypeNode (it's impossible to check)
|
||||
// https://github.com/apple/pkl/issues/639
|
||||
Object value = valueNode.executeGeneric(frame);
|
||||
try {
|
||||
typeNode.execute(frame, value);
|
||||
typeNode.executeEagerly(frame, value);
|
||||
return true;
|
||||
} catch (VmTypeMismatchException e) {
|
||||
return false;
|
||||
|
||||
@@ -219,11 +219,11 @@ public abstract class UnresolvedTypeNode extends PklNode {
|
||||
checkNumberOfTypeArguments(clazz);
|
||||
|
||||
if (clazz.isCollectionClass()) {
|
||||
return CollectionTypeNodeGen.create(sourceSection, typeArgumentNodes[0].execute(frame));
|
||||
return new CollectionTypeNode(sourceSection, typeArgumentNodes[0].execute(frame));
|
||||
}
|
||||
|
||||
if (clazz.isListClass()) {
|
||||
return ListTypeNodeGen.create(sourceSection, typeArgumentNodes[0].execute(frame));
|
||||
return new ListTypeNode(sourceSection, typeArgumentNodes[0].execute(frame));
|
||||
}
|
||||
|
||||
if (clazz.isSetClass()) {
|
||||
@@ -231,25 +231,25 @@ public abstract class UnresolvedTypeNode extends PklNode {
|
||||
}
|
||||
|
||||
if (clazz.isMapClass()) {
|
||||
return MapTypeNodeGen.create(
|
||||
return new MapTypeNode(
|
||||
sourceSection,
|
||||
typeArgumentNodes[0].execute(frame),
|
||||
typeArgumentNodes[1].execute(frame));
|
||||
}
|
||||
|
||||
if (clazz.isListingClass()) {
|
||||
return ListingTypeNodeGen.create(sourceSection, typeArgumentNodes[0].execute(frame));
|
||||
return new ListingTypeNode(sourceSection, typeArgumentNodes[0].execute(frame));
|
||||
}
|
||||
|
||||
if (clazz.isMappingClass()) {
|
||||
return MappingTypeNodeGen.create(
|
||||
return new MappingTypeNode(
|
||||
sourceSection,
|
||||
typeArgumentNodes[0].execute(frame),
|
||||
typeArgumentNodes[1].execute(frame));
|
||||
}
|
||||
|
||||
if (clazz.isPairClass()) {
|
||||
return PairTypeNodeGen.create(
|
||||
return new PairTypeNode(
|
||||
sourceSection,
|
||||
typeArgumentNodes[0].execute(frame),
|
||||
typeArgumentNodes[1].execute(frame));
|
||||
@@ -324,7 +324,7 @@ public abstract class UnresolvedTypeNode extends PklNode {
|
||||
public TypeNode execute(VirtualFrame frame) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
|
||||
return NullableTypeNodeGen.create(sourceSection, elementTypeNode.execute(frame));
|
||||
return new NullableTypeNode(sourceSection, elementTypeNode.execute(frame));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,16 +15,19 @@
|
||||
*/
|
||||
package org.pkl.core.ast.type;
|
||||
|
||||
import com.oracle.truffle.api.CallTarget;
|
||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import com.oracle.truffle.api.nodes.ControlFlowException;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import org.pkl.core.StackFrame;
|
||||
import org.pkl.core.ValueFormatter;
|
||||
import org.pkl.core.ast.type.TypeNode.UnionTypeNode;
|
||||
import org.pkl.core.runtime.*;
|
||||
import org.pkl.core.runtime.VmException.ProgramValue;
|
||||
import org.pkl.core.util.ErrorMessages;
|
||||
import org.pkl.core.util.Nullable;
|
||||
|
||||
/**
|
||||
* Indicates that a type check failed. [TypeNode]s use this exception instead of [VmException] to
|
||||
@@ -35,18 +38,35 @@ import org.pkl.core.util.ErrorMessages;
|
||||
public abstract class VmTypeMismatchException extends ControlFlowException {
|
||||
protected final SourceSection sourceSection;
|
||||
protected final Object actualValue;
|
||||
protected @Nullable Map<CallTarget, StackFrame> insertedStackFrames = null;
|
||||
|
||||
protected VmTypeMismatchException(SourceSection sourceSection, Object actualValue) {
|
||||
this.sourceSection = sourceSection;
|
||||
this.actualValue = actualValue;
|
||||
}
|
||||
|
||||
@TruffleBoundary
|
||||
public void putInsertedStackFrame(CallTarget callTarget, StackFrame stackFrame) {
|
||||
if (this.insertedStackFrames == null) {
|
||||
this.insertedStackFrames = new HashMap<>();
|
||||
}
|
||||
this.insertedStackFrames.put(callTarget, stackFrame);
|
||||
}
|
||||
|
||||
@TruffleBoundary
|
||||
public abstract void describe(StringBuilder builder, String indent);
|
||||
|
||||
@TruffleBoundary
|
||||
public abstract VmException toVmException();
|
||||
|
||||
protected VmExceptionBuilder exceptionBuilder() {
|
||||
var builder = new VmExceptionBuilder();
|
||||
if (insertedStackFrames != null) {
|
||||
builder.withInsertedStackFrames(insertedStackFrames);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static final class Simple extends VmTypeMismatchException {
|
||||
private final Object expectedType;
|
||||
|
||||
@@ -128,11 +148,12 @@ public abstract class VmTypeMismatchException extends ControlFlowException {
|
||||
return exceptionBuilder().build();
|
||||
}
|
||||
|
||||
private VmExceptionBuilder exceptionBuilder() {
|
||||
@Override
|
||||
protected VmExceptionBuilder exceptionBuilder() {
|
||||
var builder = new StringBuilder();
|
||||
describe(builder, "");
|
||||
|
||||
return new VmExceptionBuilder()
|
||||
return super.exceptionBuilder()
|
||||
.adhocEvalError(builder.toString())
|
||||
.withSourceSection(sourceSection);
|
||||
}
|
||||
@@ -162,11 +183,12 @@ public abstract class VmTypeMismatchException extends ControlFlowException {
|
||||
return exceptionBuilder().build();
|
||||
}
|
||||
|
||||
private VmExceptionBuilder exceptionBuilder() {
|
||||
@Override
|
||||
protected VmExceptionBuilder exceptionBuilder() {
|
||||
var builder = new StringBuilder();
|
||||
describe(builder, "");
|
||||
|
||||
return new VmExceptionBuilder()
|
||||
return super.exceptionBuilder()
|
||||
.adhocEvalError(builder.toString())
|
||||
.withSourceSection(sourceSection);
|
||||
}
|
||||
@@ -199,14 +221,15 @@ public abstract class VmTypeMismatchException extends ControlFlowException {
|
||||
return exceptionBuilder().build();
|
||||
}
|
||||
|
||||
private VmExceptionBuilder exceptionBuilder() {
|
||||
@Override
|
||||
protected VmExceptionBuilder exceptionBuilder() {
|
||||
var summary = new StringBuilder();
|
||||
describeSummary(summary, "");
|
||||
|
||||
var details = new StringBuilder();
|
||||
describeDetails(details, "");
|
||||
|
||||
return new VmExceptionBuilder()
|
||||
return super.exceptionBuilder()
|
||||
.adhocEvalError(summary.toString())
|
||||
.withSourceSection(sourceSection)
|
||||
.withHint(details.toString());
|
||||
@@ -304,11 +327,12 @@ public abstract class VmTypeMismatchException extends ControlFlowException {
|
||||
return exceptionBuilder().build();
|
||||
}
|
||||
|
||||
private VmExceptionBuilder exceptionBuilder() {
|
||||
@Override
|
||||
protected VmExceptionBuilder exceptionBuilder() {
|
||||
var builder = new StringBuilder();
|
||||
describe(builder, "");
|
||||
|
||||
return new VmExceptionBuilder()
|
||||
return super.exceptionBuilder()
|
||||
.adhocEvalError(builder.toString())
|
||||
.withSourceSection(sourceSection);
|
||||
}
|
||||
|
||||
@@ -79,19 +79,25 @@ public final class TestRunner {
|
||||
var factsMapping = (VmMapping) facts;
|
||||
factsMapping.forceAndIterateMemberValues(
|
||||
(groupKey, groupMember, groupValue) -> {
|
||||
var listing = (VmListing) groupValue;
|
||||
var result = results.newResult(String.valueOf(groupKey));
|
||||
var groupListing = (VmListing) groupValue;
|
||||
groupListing.forceAndIterateMemberValues(
|
||||
((factIndex, factMember, factValue) -> {
|
||||
assert factValue instanceof Boolean;
|
||||
if (factValue == Boolean.FALSE) {
|
||||
result.addFailure(
|
||||
Failure.buildFactFailure(
|
||||
factMember.getSourceSection(), getDisplayUri(factMember)));
|
||||
return listing.iterateMembers(
|
||||
(idx, member) -> {
|
||||
if (member.isLocalOrExternalOrHidden()) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
var factValue = VmUtils.readMember(listing, idx);
|
||||
if (factValue == Boolean.FALSE) {
|
||||
result.addFailure(
|
||||
Failure.buildFactFailure(member.getSourceSection(), getDisplayUri(member)));
|
||||
}
|
||||
} catch (VmException err) {
|
||||
result.addError(
|
||||
new Error(err.getMessage(), err.toPklException(stackFrameTransformer)));
|
||||
}
|
||||
return true;
|
||||
}));
|
||||
return true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -142,12 +148,14 @@ public final class TestRunner {
|
||||
var expectedExampleOutputs = loadExampleOutputs(expectedOutputFile);
|
||||
var actualExampleOutputs = new MutableReference<VmDynamic>(null);
|
||||
var allGroupsSucceeded = new MutableBoolean(true);
|
||||
var errored = new MutableBoolean(false);
|
||||
examples.forceAndIterateMemberValues(
|
||||
(groupKey, groupMember, groupValue) -> {
|
||||
var testName = String.valueOf(groupKey);
|
||||
var group = (VmListing) groupValue;
|
||||
var expectedGroup =
|
||||
(VmDynamic) VmUtils.readMemberOrNull(expectedExampleOutputs, groupKey);
|
||||
var result = results.newResult(testName);
|
||||
|
||||
if (expectedGroup == null) {
|
||||
results.newResult(
|
||||
@@ -158,8 +166,7 @@ public final class TestRunner {
|
||||
}
|
||||
|
||||
if (group.getLength() != expectedGroup.getLength()) {
|
||||
results.newResult(
|
||||
testName,
|
||||
result.addFailure(
|
||||
Failure.buildExampleLengthMismatchFailure(
|
||||
getDisplayUri(groupMember),
|
||||
String.valueOf(groupKey),
|
||||
@@ -169,8 +176,21 @@ public final class TestRunner {
|
||||
}
|
||||
|
||||
var groupSucceeded = new MutableBoolean(true);
|
||||
group.forceAndIterateMemberValues(
|
||||
((exampleIndex, exampleMember, exampleValue) -> {
|
||||
group.iterateMembers(
|
||||
((exampleIndex, exampleMember) -> {
|
||||
if (exampleMember.isLocalOrExternalOrHidden()) {
|
||||
return true;
|
||||
}
|
||||
Object exampleValue;
|
||||
try {
|
||||
exampleValue = VmUtils.readMember(group, exampleIndex);
|
||||
} catch (VmException err) {
|
||||
errored.set(true);
|
||||
result.addError(
|
||||
new Error(err.getMessage(), err.toPklException(stackFrameTransformer)));
|
||||
groupSucceeded.set(false);
|
||||
return true;
|
||||
}
|
||||
var expectedValue = VmUtils.readMember(expectedGroup, exampleIndex);
|
||||
|
||||
var exampleValuePcf = renderAsPcf(exampleValue);
|
||||
@@ -202,8 +222,7 @@ public final class TestRunner {
|
||||
.build();
|
||||
}
|
||||
|
||||
results.newResult(
|
||||
testName,
|
||||
result.addFailure(
|
||||
Failure.buildExampleFailure(
|
||||
getDisplayUri(exampleMember),
|
||||
getDisplayUri(expectedMember),
|
||||
@@ -215,9 +234,7 @@ public final class TestRunner {
|
||||
return true;
|
||||
}));
|
||||
|
||||
if (groupSucceeded.get()) {
|
||||
results.newResult(testName);
|
||||
} else {
|
||||
if (!groupSucceeded.get()) {
|
||||
allGroupsSucceeded.set(false);
|
||||
}
|
||||
|
||||
@@ -231,26 +248,52 @@ public final class TestRunner {
|
||||
}
|
||||
if (examples.getCachedValue(groupKey) == null) {
|
||||
allGroupsSucceeded.set(false);
|
||||
results.newResult(
|
||||
String.valueOf(groupKey),
|
||||
Failure.buildExamplePropertyMismatchFailure(
|
||||
getDisplayUri(groupMember), String.valueOf(groupKey), false));
|
||||
results
|
||||
.newResult(String.valueOf(groupKey))
|
||||
.addFailure(
|
||||
Failure.buildExamplePropertyMismatchFailure(
|
||||
getDisplayUri(groupMember), String.valueOf(groupKey), false));
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!allGroupsSucceeded.get() && actualExampleOutputs.isNull()) {
|
||||
if (!allGroupsSucceeded.get() && actualExampleOutputs.isNull() && !errored.get()) {
|
||||
writeExampleOutputs(actualOutputFile, examples);
|
||||
}
|
||||
}
|
||||
|
||||
private void doRunAndWriteExamples(VmMapping examples, Path outputFile, TestResults results) {
|
||||
examples.forceAndIterateMemberValues(
|
||||
(groupKey, groupMember, groupValue) -> {
|
||||
results.newResult(String.valueOf(groupKey)).setExampleWritten(true);
|
||||
return true;
|
||||
});
|
||||
writeExampleOutputs(outputFile, examples);
|
||||
var allSucceeded =
|
||||
examples.forceAndIterateMemberValues(
|
||||
(groupKey, groupMember, groupValue) -> {
|
||||
var listing = (VmListing) groupValue;
|
||||
var success =
|
||||
listing.iterateMembers(
|
||||
(idx, member) -> {
|
||||
if (member.isLocalOrExternalOrHidden()) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
VmUtils.readMember(listing, idx);
|
||||
return true;
|
||||
} catch (VmException err) {
|
||||
results
|
||||
.newResult(String.valueOf(groupKey))
|
||||
.addError(
|
||||
new Error(
|
||||
err.getMessage(), err.toPklException(stackFrameTransformer)));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
results.newResult(String.valueOf(groupKey)).setExampleWritten(true);
|
||||
return true;
|
||||
});
|
||||
if (allSucceeded) {
|
||||
writeExampleOutputs(outputFile, examples);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeExampleOutputs(Path outputFile, VmMapping examples) {
|
||||
|
||||
@@ -15,10 +15,12 @@
|
||||
*/
|
||||
package org.pkl.core.runtime;
|
||||
|
||||
import com.oracle.truffle.api.CallTarget;
|
||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.pkl.core.*;
|
||||
import org.pkl.core.util.Nullable;
|
||||
|
||||
@@ -32,7 +34,8 @@ public final class VmBugException extends VmException {
|
||||
@Nullable Node location,
|
||||
@Nullable SourceSection sourceSection,
|
||||
@Nullable String memberName,
|
||||
@Nullable String hint) {
|
||||
@Nullable String hint,
|
||||
Map<CallTarget, StackFrame> insertedStackFrames) {
|
||||
|
||||
super(
|
||||
message,
|
||||
@@ -43,7 +46,8 @@ public final class VmBugException extends VmException {
|
||||
location,
|
||||
sourceSection,
|
||||
memberName,
|
||||
hint);
|
||||
hint,
|
||||
insertedStackFrames);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -15,9 +15,12 @@
|
||||
*/
|
||||
package org.pkl.core.runtime;
|
||||
|
||||
import com.oracle.truffle.api.CallTarget;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.pkl.core.StackFrame;
|
||||
import org.pkl.core.util.Nullable;
|
||||
|
||||
public class VmEvalException extends VmException {
|
||||
@@ -30,7 +33,8 @@ public class VmEvalException extends VmException {
|
||||
@Nullable Node location,
|
||||
@Nullable SourceSection sourceSection,
|
||||
@Nullable String memberName,
|
||||
@Nullable String hint) {
|
||||
@Nullable String hint,
|
||||
Map<CallTarget, StackFrame> insertedStackFrames) {
|
||||
|
||||
super(
|
||||
message,
|
||||
@@ -41,6 +45,7 @@ public class VmEvalException extends VmException {
|
||||
location,
|
||||
sourceSection,
|
||||
memberName,
|
||||
hint);
|
||||
hint,
|
||||
insertedStackFrames);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import com.oracle.truffle.api.exception.AbstractTruffleException;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.pkl.core.*;
|
||||
@@ -33,8 +32,7 @@ public abstract class VmException extends AbstractTruffleException {
|
||||
private final @Nullable SourceSection sourceSection;
|
||||
private final @Nullable String memberName;
|
||||
protected @Nullable String hint;
|
||||
|
||||
private final Map<CallTarget, StackFrame> insertedStackFrames = new HashMap<>();
|
||||
private final Map<CallTarget, StackFrame> insertedStackFrames;
|
||||
|
||||
public VmException(
|
||||
String message,
|
||||
@@ -45,7 +43,8 @@ public abstract class VmException extends AbstractTruffleException {
|
||||
@Nullable Node location,
|
||||
@Nullable SourceSection sourceSection,
|
||||
@Nullable String memberName,
|
||||
@Nullable String hint) {
|
||||
@Nullable String hint,
|
||||
Map<CallTarget, StackFrame> insertedStackFrames) {
|
||||
super(message, cause, UNLIMITED_STACK_TRACE, location);
|
||||
this.isExternalMessage = isExternalMessage;
|
||||
this.messageArguments = messageArguments;
|
||||
@@ -53,6 +52,7 @@ public abstract class VmException extends AbstractTruffleException {
|
||||
this.sourceSection = sourceSection;
|
||||
this.memberName = memberName;
|
||||
this.hint = hint;
|
||||
this.insertedStackFrames = insertedStackFrames;
|
||||
}
|
||||
|
||||
public final boolean isExternalMessage() {
|
||||
|
||||
@@ -15,11 +15,13 @@
|
||||
*/
|
||||
package org.pkl.core.runtime;
|
||||
|
||||
import com.oracle.truffle.api.CallTarget;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.pkl.core.StackFrame;
|
||||
import org.pkl.core.runtime.MemberLookupSuggestions.Candidate.Kind;
|
||||
import org.pkl.core.runtime.VmException.ProgramValue;
|
||||
import org.pkl.core.util.Nullable;
|
||||
@@ -50,6 +52,7 @@ import org.pkl.core.util.Nullable;
|
||||
public final class VmExceptionBuilder {
|
||||
|
||||
private @Nullable Object receiver;
|
||||
private @Nullable Map<CallTarget, StackFrame> insertedStackFrames;
|
||||
|
||||
public static class MultilineValue {
|
||||
private final Iterable<?> lines;
|
||||
@@ -329,11 +332,19 @@ public final class VmExceptionBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public VmExceptionBuilder withInsertedStackFrames(
|
||||
Map<CallTarget, StackFrame> insertedStackFrames) {
|
||||
this.insertedStackFrames = insertedStackFrames;
|
||||
return this;
|
||||
}
|
||||
|
||||
public VmException build() {
|
||||
if (message == null) {
|
||||
throw new IllegalStateException("No message set.");
|
||||
}
|
||||
|
||||
var effectiveInsertedStackFrames =
|
||||
insertedStackFrames == null ? new HashMap<CallTarget, StackFrame>() : insertedStackFrames;
|
||||
return switch (kind) {
|
||||
case EVAL_ERROR ->
|
||||
new VmEvalException(
|
||||
@@ -345,7 +356,8 @@ public final class VmExceptionBuilder {
|
||||
location,
|
||||
sourceSection,
|
||||
memberName,
|
||||
hint);
|
||||
hint,
|
||||
effectiveInsertedStackFrames);
|
||||
case UNDEFINED_VALUE ->
|
||||
new VmUndefinedValueException(
|
||||
message,
|
||||
@@ -357,7 +369,8 @@ public final class VmExceptionBuilder {
|
||||
sourceSection,
|
||||
memberName,
|
||||
hint,
|
||||
receiver);
|
||||
receiver,
|
||||
effectiveInsertedStackFrames);
|
||||
case BUG ->
|
||||
new VmBugException(
|
||||
message,
|
||||
@@ -368,7 +381,8 @@ public final class VmExceptionBuilder {
|
||||
location,
|
||||
sourceSection,
|
||||
memberName,
|
||||
hint);
|
||||
hint,
|
||||
effectiveInsertedStackFrames);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ public final class VmFunction extends VmObjectLike {
|
||||
|
||||
@Override
|
||||
@TruffleBoundary
|
||||
public void setCachedValue(Object key, Object value) {
|
||||
public void setCachedValue(Object key, Object value, ObjectMember objectMember) {
|
||||
throw new VmExceptionBuilder()
|
||||
.bug("Class `VmFunction` does not support method `setCachedValue()`.")
|
||||
.build();
|
||||
|
||||
@@ -21,13 +21,12 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import org.graalvm.collections.UnmodifiableEconomicMap;
|
||||
import org.pkl.core.ast.member.ListingOrMappingTypeCastNode;
|
||||
import org.pkl.core.ast.member.ObjectMember;
|
||||
import org.pkl.core.util.EconomicMaps;
|
||||
import org.pkl.core.util.Nullable;
|
||||
|
||||
// TODO: make sure that "default" isn't forced
|
||||
// when a listing is rendered ("default" should be allowed to be partial)
|
||||
public final class VmListing extends VmObject {
|
||||
public final class VmListing extends VmListingOrMapping<VmListing> {
|
||||
private static final class EmptyHolder {
|
||||
private static final VmListing EMPTY =
|
||||
new VmListing(
|
||||
@@ -48,7 +47,25 @@ public final class VmListing extends VmObject {
|
||||
VmObject parent,
|
||||
UnmodifiableEconomicMap<Object, ObjectMember> members,
|
||||
int length) {
|
||||
super(enclosingFrame, Objects.requireNonNull(parent), members);
|
||||
super(enclosingFrame, Objects.requireNonNull(parent), members, null, null, null);
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public VmListing(
|
||||
MaterializedFrame enclosingFrame,
|
||||
VmObject parent,
|
||||
UnmodifiableEconomicMap<Object, ObjectMember> members,
|
||||
int length,
|
||||
@Nullable VmListing delegate,
|
||||
ListingOrMappingTypeCastNode typeCheckNode,
|
||||
MaterializedFrame typeNodeFrame) {
|
||||
super(
|
||||
enclosingFrame,
|
||||
Objects.requireNonNull(parent),
|
||||
members,
|
||||
delegate,
|
||||
typeCheckNode,
|
||||
typeNodeFrame);
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
@@ -100,6 +117,20 @@ public final class VmListing extends VmObject {
|
||||
return converter.convertListing(this, path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VmListing withCheckedMembers(
|
||||
ListingOrMappingTypeCastNode typeCheckNode, MaterializedFrame typeNodeFrame) {
|
||||
|
||||
return new VmListing(
|
||||
getEnclosingFrame(),
|
||||
Objects.requireNonNull(parent),
|
||||
members,
|
||||
length,
|
||||
this,
|
||||
typeCheckNode,
|
||||
typeNodeFrame);
|
||||
}
|
||||
|
||||
@Override
|
||||
@TruffleBoundary
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
* Copyright © 2024 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;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.frame.MaterializedFrame;
|
||||
import com.oracle.truffle.api.nodes.IndirectCallNode;
|
||||
import org.graalvm.collections.EconomicMap;
|
||||
import org.graalvm.collections.EconomicSet;
|
||||
import org.graalvm.collections.UnmodifiableEconomicMap;
|
||||
import org.pkl.core.PklBugException;
|
||||
import org.pkl.core.ast.member.ListingOrMappingTypeCastNode;
|
||||
import org.pkl.core.ast.member.ObjectMember;
|
||||
import org.pkl.core.ast.type.TypeNode;
|
||||
import org.pkl.core.util.EconomicMaps;
|
||||
import org.pkl.core.util.EconomicSets;
|
||||
import org.pkl.core.util.Nullable;
|
||||
|
||||
public abstract class VmListingOrMapping<SELF extends VmListingOrMapping<SELF>> extends VmObject {
|
||||
|
||||
/**
|
||||
* A Listing or Mapping typecast creates a new object that contains a new typecheck node, and
|
||||
* delegates member lookups to this delegate.
|
||||
*/
|
||||
private final @Nullable SELF delegate;
|
||||
|
||||
private final @Nullable ListingOrMappingTypeCastNode typeCastNode;
|
||||
private final MaterializedFrame typeNodeFrame;
|
||||
private final EconomicMap<Object, ObjectMember> cachedMembers = EconomicMaps.create();
|
||||
private final EconomicSet<Object> checkedMembers = EconomicSets.create();
|
||||
|
||||
public VmListingOrMapping(
|
||||
MaterializedFrame enclosingFrame,
|
||||
@Nullable VmObject parent,
|
||||
UnmodifiableEconomicMap<Object, ObjectMember> members,
|
||||
@Nullable SELF delegate,
|
||||
@Nullable ListingOrMappingTypeCastNode typeCastNode,
|
||||
@Nullable MaterializedFrame typeNodeFrame) {
|
||||
super(enclosingFrame, parent, members);
|
||||
this.delegate = delegate;
|
||||
this.typeCastNode = typeCastNode;
|
||||
this.typeNodeFrame = typeNodeFrame;
|
||||
}
|
||||
|
||||
ObjectMember findMember(Object key) {
|
||||
var member = EconomicMaps.get(cachedMembers, key);
|
||||
if (member != null) {
|
||||
return member;
|
||||
}
|
||||
if (delegate != null) {
|
||||
return delegate.findMember(key);
|
||||
}
|
||||
// member is guaranteed to exist; this is only called if `getCachedValue()` returns non-null
|
||||
// and `setCachedValue` will record the object member in `cachedMembers`.
|
||||
throw PklBugException.unreachableCode();
|
||||
}
|
||||
|
||||
public @Nullable ListingOrMappingTypeCastNode getTypeCastNode() {
|
||||
return typeCastNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCachedValue(Object key, Object value, ObjectMember objectMember) {
|
||||
super.setCachedValue(key, value, objectMember);
|
||||
EconomicMaps.put(cachedMembers, key, objectMember);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Object getCachedValue(Object key) {
|
||||
var myCachedValue = super.getCachedValue(key);
|
||||
if (myCachedValue != null || delegate == null) {
|
||||
return myCachedValue;
|
||||
}
|
||||
var memberValue = delegate.getCachedValue(key);
|
||||
// if this object member appears inside `checkedMembers`, we have already checked its type
|
||||
// and can safely return it.
|
||||
if (EconomicSets.contains(checkedMembers, key)) {
|
||||
return memberValue;
|
||||
}
|
||||
if (memberValue == null) {
|
||||
return null;
|
||||
}
|
||||
// If a cached value already exists on the delegate, run a typecast on it.
|
||||
// optimization: don't use `VmUtils.findMember` to avoid iterating over all members
|
||||
var objectMember = findMember(key);
|
||||
var ret = typecastObjectMember(objectMember, memberValue, IndirectCallNode.getUncached());
|
||||
if (ret != memberValue) {
|
||||
EconomicMaps.put(cachedValues, key, ret);
|
||||
} else {
|
||||
// optimization: don't add to own cached values if typecast results in the same value
|
||||
EconomicSets.add(checkedMembers, key);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getExtraStorage() {
|
||||
if (delegate != null) {
|
||||
return delegate.getExtraStorage();
|
||||
}
|
||||
assert extraStorage != null;
|
||||
return extraStorage;
|
||||
}
|
||||
|
||||
/** Perform a typecast on this member, */
|
||||
public Object typecastObjectMember(
|
||||
ObjectMember member, Object memberValue, IndirectCallNode callNode) {
|
||||
if (!(member.isEntry() || member.isElement()) || typeCastNode == null) {
|
||||
return memberValue;
|
||||
}
|
||||
assert typeNodeFrame != null;
|
||||
var ret = memberValue;
|
||||
if (delegate != null) {
|
||||
ret = delegate.typecastObjectMember(member, ret, callNode);
|
||||
}
|
||||
var callTarget = typeCastNode.getCallTarget();
|
||||
try {
|
||||
return callNode.call(
|
||||
callTarget, VmUtils.getReceiver(typeNodeFrame), VmUtils.getOwner(typeNodeFrame), ret);
|
||||
} catch (VmException vmException) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
// treat typecheck as part of the call stack to read the original member if there is a
|
||||
// source section for it.
|
||||
var sourceSection = member.getBodySection();
|
||||
if (!sourceSection.isAvailable()) {
|
||||
sourceSection = member.getSourceSection();
|
||||
}
|
||||
if (sourceSection.isAvailable()) {
|
||||
vmException
|
||||
.getInsertedStackFrames()
|
||||
.put(callTarget, VmUtils.createStackFrame(sourceSection, member.getQualifiedName()));
|
||||
}
|
||||
throw vmException;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract SELF withCheckedMembers(
|
||||
ListingOrMappingTypeCastNode typeCastNode, MaterializedFrame typeNodeFrame);
|
||||
|
||||
/** Tells if this mapping/listing runs the same typechecks as {@code typeNode}. */
|
||||
public boolean hasSameChecksAs(TypeNode typeNode) {
|
||||
if (typeCastNode == null) {
|
||||
return false;
|
||||
}
|
||||
if (typeCastNode.getTypeNode().isEquivalentTo(typeNode)) {
|
||||
return true;
|
||||
}
|
||||
// we can say the check is the same if the delegate has this check.
|
||||
// when `Listing<Any>` delegates to `Listing<UInt>`, it has the same checks as a `UInt`
|
||||
// typenode.
|
||||
if (delegate != null) {
|
||||
return delegate.hasSameChecksAs(typeNode);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -21,14 +21,14 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
import org.graalvm.collections.UnmodifiableEconomicMap;
|
||||
import org.pkl.core.ast.member.ListingOrMappingTypeCastNode;
|
||||
import org.pkl.core.ast.member.ObjectMember;
|
||||
import org.pkl.core.util.CollectionUtils;
|
||||
import org.pkl.core.util.EconomicMaps;
|
||||
import org.pkl.core.util.LateInit;
|
||||
|
||||
// TODO: make sure that "default" isn't forced
|
||||
// when a mapping is rendered ("default" should be allowed to be partial)
|
||||
public final class VmMapping extends VmObject {
|
||||
public final class VmMapping extends VmListingOrMapping<VmMapping> {
|
||||
|
||||
private int cachedEntryCount = -1;
|
||||
|
||||
@GuardedBy("this")
|
||||
@@ -51,7 +51,23 @@ public final class VmMapping extends VmObject {
|
||||
VmObject parent,
|
||||
UnmodifiableEconomicMap<Object, ObjectMember> members) {
|
||||
|
||||
super(enclosingFrame, Objects.requireNonNull(parent), members);
|
||||
super(enclosingFrame, Objects.requireNonNull(parent), members, null, null, null);
|
||||
}
|
||||
|
||||
public VmMapping(
|
||||
MaterializedFrame enclosingFrame,
|
||||
VmObject parent,
|
||||
UnmodifiableEconomicMap<Object, ObjectMember> members,
|
||||
VmMapping delegate,
|
||||
ListingOrMappingTypeCastNode typeCheckNode,
|
||||
MaterializedFrame typeNodeFrame) {
|
||||
super(
|
||||
enclosingFrame,
|
||||
Objects.requireNonNull(parent),
|
||||
members,
|
||||
delegate,
|
||||
typeCheckNode,
|
||||
typeNodeFrame);
|
||||
}
|
||||
|
||||
public static boolean isDefaultProperty(Object propertyKey) {
|
||||
@@ -181,4 +197,17 @@ public final class VmMapping extends VmObject {
|
||||
cachedEntryCount = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@TruffleBoundary
|
||||
public VmMapping withCheckedMembers(
|
||||
ListingOrMappingTypeCastNode typeCheckNode, MaterializedFrame typeNodeFrame) {
|
||||
return new VmMapping(
|
||||
getEnclosingFrame(),
|
||||
Objects.requireNonNull(getParent()),
|
||||
getMembers(),
|
||||
this,
|
||||
typeCheckNode,
|
||||
typeNodeFrame);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,12 +82,12 @@ public abstract class VmObject extends VmObjectLike {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final @Nullable Object getCachedValue(Object key) {
|
||||
public @Nullable Object getCachedValue(Object key) {
|
||||
return EconomicMaps.get(cachedValues, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setCachedValue(Object key, Object value) {
|
||||
public void setCachedValue(Object key, Object value, ObjectMember objectMember) {
|
||||
EconomicMaps.put(cachedValues, key, value);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ public abstract class VmObjectLike extends VmValue {
|
||||
return extraStorage != null;
|
||||
}
|
||||
|
||||
public final Object getExtraStorage() {
|
||||
public Object getExtraStorage() {
|
||||
assert extraStorage != null;
|
||||
return extraStorage;
|
||||
}
|
||||
@@ -96,7 +96,7 @@ public abstract class VmObjectLike extends VmValue {
|
||||
* receiver.
|
||||
*/
|
||||
@TruffleBoundary
|
||||
public abstract void setCachedValue(Object key, Object value);
|
||||
public abstract void setCachedValue(Object key, Object value, ObjectMember objectMember);
|
||||
|
||||
/**
|
||||
* Prefer this method over {@link #getCachedValue} if the value is not required. (There is no
|
||||
|
||||
@@ -15,11 +15,22 @@
|
||||
*/
|
||||
package org.pkl.core.runtime;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public final class VmStackOverflowException extends VmException {
|
||||
|
||||
public VmStackOverflowException(StackOverflowError e) {
|
||||
super("stackOverflow", e, true, new Object[0], List.of(), null, null, null, null);
|
||||
super(
|
||||
"stackOverflow",
|
||||
e,
|
||||
true,
|
||||
new Object[0],
|
||||
List.of(),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new HashMap<>());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,13 @@
|
||||
*/
|
||||
package org.pkl.core.runtime;
|
||||
|
||||
import com.oracle.truffle.api.CallTarget;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.pkl.core.StackFrame;
|
||||
import org.pkl.core.parser.Lexer;
|
||||
import org.pkl.core.util.Nullable;
|
||||
|
||||
@@ -35,7 +38,8 @@ public final class VmUndefinedValueException extends VmEvalException {
|
||||
@Nullable SourceSection sourceSection,
|
||||
@Nullable String memberName,
|
||||
@Nullable String hint,
|
||||
@Nullable Object receiver) {
|
||||
@Nullable Object receiver,
|
||||
Map<CallTarget, StackFrame> insertedStackFrames) {
|
||||
|
||||
super(
|
||||
message,
|
||||
@@ -46,7 +50,8 @@ public final class VmUndefinedValueException extends VmEvalException {
|
||||
location,
|
||||
sourceSection,
|
||||
memberName,
|
||||
hint);
|
||||
hint,
|
||||
insertedStackFrames);
|
||||
|
||||
this.receiver = receiver;
|
||||
}
|
||||
|
||||
@@ -263,6 +263,7 @@ public final class VmUtils {
|
||||
|
||||
final var constantValue = member.getConstantValue();
|
||||
if (constantValue != null) {
|
||||
var ret = constantValue;
|
||||
// for a property, do a type check
|
||||
if (member.isProp()) {
|
||||
var property = receiver.getVmClass().getProperty(member.getName());
|
||||
@@ -270,10 +271,15 @@ public final class VmUtils {
|
||||
var callTarget = property.getTypeNode().getCallTarget();
|
||||
try {
|
||||
if (checkType) {
|
||||
callNode.call(callTarget, receiver, property.getOwner(), constantValue);
|
||||
ret = callNode.call(callTarget, receiver, property.getOwner(), constantValue);
|
||||
} else {
|
||||
callNode.call(
|
||||
callTarget, receiver, property.getOwner(), constantValue, SKIP_TYPECHECK_MARKER);
|
||||
ret =
|
||||
callNode.call(
|
||||
callTarget,
|
||||
receiver,
|
||||
property.getOwner(),
|
||||
constantValue,
|
||||
VmUtils.SKIP_TYPECHECK_MARKER);
|
||||
}
|
||||
} catch (VmException e) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
@@ -293,21 +299,25 @@ public final class VmUtils {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
} else if (receiver instanceof VmListingOrMapping<?> vmListingOrMapping) {
|
||||
ret = vmListingOrMapping.typecastObjectMember(member, ret, callNode);
|
||||
}
|
||||
|
||||
receiver.setCachedValue(memberKey, constantValue);
|
||||
return constantValue;
|
||||
receiver.setCachedValue(memberKey, ret, member);
|
||||
return ret;
|
||||
}
|
||||
|
||||
var callTarget = member.getCallTarget();
|
||||
Object computedValue;
|
||||
Object ret;
|
||||
if (checkType) {
|
||||
computedValue = callNode.call(callTarget, receiver, owner, memberKey);
|
||||
ret = callNode.call(callTarget, receiver, owner, memberKey);
|
||||
} else {
|
||||
computedValue = callNode.call(callTarget, receiver, owner, memberKey, SKIP_TYPECHECK_MARKER);
|
||||
ret = callNode.call(callTarget, receiver, owner, memberKey, VmUtils.SKIP_TYPECHECK_MARKER);
|
||||
}
|
||||
receiver.setCachedValue(memberKey, computedValue);
|
||||
return computedValue;
|
||||
if (receiver instanceof VmListingOrMapping<?> vmListingOrMapping) {
|
||||
ret = vmListingOrMapping.typecastObjectMember(member, ret, callNode);
|
||||
}
|
||||
receiver.setCachedValue(memberKey, ret, member);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static @Nullable ObjectMember findMember(VmObjectLike receiver, Object memberKey) {
|
||||
@@ -849,4 +859,17 @@ public final class VmUtils {
|
||||
public static <K, V> V getMapValue(Map<K, V> map, K key) {
|
||||
return map.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, the value computed by this node is not the final value exposed to user code but will
|
||||
* still be amended.
|
||||
*
|
||||
* <p>Used to disable type check for to-be-amended properties. See {@link
|
||||
* org.pkl.core.runtime.VmUtils#SKIP_TYPECHECK_MARKER}. IDEA: might be more appropriate to only
|
||||
* skip constraints check
|
||||
*/
|
||||
public static boolean shouldRunTypeCheck(VirtualFrame frame) {
|
||||
return frame.getArguments().length != 4
|
||||
|| frame.getArguments()[3] != VmUtils.SKIP_TYPECHECK_MARKER;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.pkl.core.stdlib.base;
|
||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.nodes.IndirectCallNode;
|
||||
import java.util.HashSet;
|
||||
import org.pkl.core.ast.lambda.ApplyVmFunction3Node;
|
||||
import org.pkl.core.ast.lambda.ApplyVmFunction3NodeGen;
|
||||
import org.pkl.core.runtime.*;
|
||||
@@ -50,9 +51,14 @@ public final class MappingNodes {
|
||||
@Specialization
|
||||
@TruffleBoundary
|
||||
protected long eval(VmMapping self) {
|
||||
MutableLong count = new MutableLong(0);
|
||||
self.iterateMemberValues(
|
||||
(key, member, value) -> {
|
||||
var count = new MutableLong(0);
|
||||
var visited = new HashSet<>();
|
||||
self.iterateMembers(
|
||||
(key, member) -> {
|
||||
var alreadyVisited = !visited.add(key);
|
||||
// important to record hidden member as visited before skipping it
|
||||
// because any overriding member won't carry a `hidden` identifier
|
||||
if (alreadyVisited || member.isLocalOrExternalOrHidden()) return true;
|
||||
count.getAndIncrement();
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -42,8 +42,6 @@ import org.pkl.core.ast.type.TypeNode.StringTypeNode;
|
||||
import org.pkl.core.ast.type.TypeNode.TypeAliasTypeNode;
|
||||
import org.pkl.core.ast.type.TypeNode.UnionOfStringLiteralsTypeNode;
|
||||
import org.pkl.core.ast.type.TypeNode.UnionTypeNode;
|
||||
import org.pkl.core.ast.type.TypeNodeFactory.ListingTypeNodeGen;
|
||||
import org.pkl.core.ast.type.TypeNodeFactory.MappingTypeNodeGen;
|
||||
import org.pkl.core.ast.type.VmTypeMismatchException;
|
||||
import org.pkl.core.runtime.Identifier;
|
||||
import org.pkl.core.runtime.VmClass;
|
||||
@@ -575,7 +573,7 @@ public final class RendererNodes {
|
||||
type =
|
||||
requiresWrapper()
|
||||
? null
|
||||
: ListingTypeNodeGen.create(VmUtils.unavailableSourceSection(), valueType);
|
||||
: new ListingTypeNode(VmUtils.unavailableSourceSection(), valueType);
|
||||
return type;
|
||||
} else if (type instanceof MappingTypeNode mappingType) {
|
||||
var keyType = resolveType(mappingType.getKeyTypeNode());
|
||||
@@ -589,8 +587,7 @@ public final class RendererNodes {
|
||||
}
|
||||
var valueType = resolveType(mappingType.getValueTypeNode());
|
||||
assert valueType != null : "Incomplete or malformed Mapping type";
|
||||
mappingType =
|
||||
MappingTypeNodeGen.create(VmUtils.unavailableSourceSection(), keyType, valueType);
|
||||
mappingType = new MappingTypeNode(VmUtils.unavailableSourceSection(), keyType, valueType);
|
||||
|
||||
type = requiresWrapper() ? null : mappingType;
|
||||
return type;
|
||||
|
||||
@@ -30,4 +30,9 @@ public final class EconomicSets {
|
||||
public static <E> boolean add(EconomicSet<E> self, E element) {
|
||||
return self.add(element);
|
||||
}
|
||||
|
||||
@TruffleBoundary
|
||||
public static <E> boolean contains(EconomicSet<E> self, E element) {
|
||||
return self.contains(element);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,9 @@ examples {
|
||||
|
||||
["listing"] {
|
||||
new Listing { 1; 2; 3 } as Listing<Int>
|
||||
module.catch(() -> new Listing { 1; 2; 3 } as Listing<String>)
|
||||
module.catch(() -> (new Listing { 1; 2; 3 } as Listing<String>)[0])
|
||||
}
|
||||
|
||||
|
||||
["mapping"] {
|
||||
module.catch(() -> new Listing { 1; 2; 3 } as Mapping<Int, String>)
|
||||
new Mapping { ["Pigeon"] = 42; ["Barn Owl"] = 21 } as Mapping<String, Int>
|
||||
@@ -49,9 +49,9 @@ examples {
|
||||
["function type"] {
|
||||
(((x) -> x) as (Int) -> Int).apply(42)
|
||||
(((x) -> x) as (String) -> String).apply(42)
|
||||
module.catch(() -> ((x, y) -> x) as (Int) -> Int)
|
||||
module.catch(() -> ((x, _) -> x) as (Int) -> Int)
|
||||
}
|
||||
|
||||
|
||||
["string literal type"] {
|
||||
"Pigeon" as "Pigeon"|"Barn Owl"
|
||||
module.catch(() -> "Pigeon" as "Piggy"|"Barn Owl")
|
||||
|
||||
34
pkl-core/src/test/files/LanguageSnippetTests/input/basic/as3.pkl
vendored
Normal file
34
pkl-core/src/test/files/LanguageSnippetTests/input/basic/as3.pkl
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
amends "../snippetTest.pkl"
|
||||
|
||||
examples {
|
||||
["set of listing"] {
|
||||
local s1 = Set(
|
||||
new Listing { 1; 2; 3 }
|
||||
) as Set<Listing<Int>>
|
||||
local s2 = Set(
|
||||
new Listing { "one"; "two"; "three" }
|
||||
) as Set<Listing<Int>>
|
||||
s1.first[0]
|
||||
module.catchOrNull(() -> s2.first) == null
|
||||
module.catch(() -> s2.first[0])
|
||||
}
|
||||
["listing"] {
|
||||
local l = new Listing { 1; 2; 3 } as Listing<String>
|
||||
module.catchOrNull(() -> l) == null
|
||||
module.catch(() -> l[0])
|
||||
}
|
||||
["mapping"] {
|
||||
local m1 = new Mapping {
|
||||
["hi"] = 1
|
||||
["bye"] = 2
|
||||
} as Mapping<String, String>
|
||||
module.catchOrNull(() -> m1) == null
|
||||
module.catch(() -> m1["hi"])
|
||||
module.catch(() -> m1["bye"])
|
||||
local m2 = new Mapping {
|
||||
["hi"] = 1
|
||||
["bye"] = 2
|
||||
} as Mapping<Int, Int>
|
||||
module.catch(() -> m2)
|
||||
}
|
||||
}
|
||||
3
pkl-core/src/test/files/LanguageSnippetTests/input/errors/listingTypeCheckError1.pkl
vendored
Normal file
3
pkl-core/src/test/files/LanguageSnippetTests/input/errors/listingTypeCheckError1.pkl
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
res = new Listing<String> {
|
||||
1
|
||||
}
|
||||
5
pkl-core/src/test/files/LanguageSnippetTests/input/errors/listingTypeCheckError2.pkl
vendored
Normal file
5
pkl-core/src/test/files/LanguageSnippetTests/input/errors/listingTypeCheckError2.pkl
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
one = 1
|
||||
|
||||
res = new Listing<String> {
|
||||
one
|
||||
}
|
||||
3
pkl-core/src/test/files/LanguageSnippetTests/input/errors/listingTypeCheckError3.pkl
vendored
Normal file
3
pkl-core/src/test/files/LanguageSnippetTests/input/errors/listingTypeCheckError3.pkl
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
res: Listing<String(!isEmpty)> = new Listing<String> {
|
||||
""
|
||||
}
|
||||
3
pkl-core/src/test/files/LanguageSnippetTests/input/errors/listingTypeCheckError4.pkl
vendored
Normal file
3
pkl-core/src/test/files/LanguageSnippetTests/input/errors/listingTypeCheckError4.pkl
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
res: Listing<String(!isEmpty)> = new Listing<String(endsWith("ga"))> {
|
||||
"hola"
|
||||
}
|
||||
4
pkl-core/src/test/files/LanguageSnippetTests/input/errors/listingTypeCheckError5.pkl
vendored
Normal file
4
pkl-core/src/test/files/LanguageSnippetTests/input/errors/listingTypeCheckError5.pkl
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
local l = new Listing { 1; 2; 3 } as Listing<String>
|
||||
|
||||
res = l[0]
|
||||
|
||||
3
pkl-core/src/test/files/LanguageSnippetTests/input/errors/mappingTypeCheckError1.pkl
vendored
Normal file
3
pkl-core/src/test/files/LanguageSnippetTests/input/errors/mappingTypeCheckError1.pkl
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
res = new Mapping<String, String> {
|
||||
["foo"] = 1
|
||||
}
|
||||
3
pkl-core/src/test/files/LanguageSnippetTests/input/errors/mappingTypeCheckError2.pkl
vendored
Normal file
3
pkl-core/src/test/files/LanguageSnippetTests/input/errors/mappingTypeCheckError2.pkl
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
res: Mapping<String, String> = new {
|
||||
["foo"] = 1
|
||||
}
|
||||
4
pkl-core/src/test/files/LanguageSnippetTests/input/errors/mappingTypeCheckError3.pkl
vendored
Normal file
4
pkl-core/src/test/files/LanguageSnippetTests/input/errors/mappingTypeCheckError3.pkl
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
// ConstantEntriesLiteralNode
|
||||
res: Mapping<String, String> = new {
|
||||
[1] = "foo"
|
||||
}
|
||||
6
pkl-core/src/test/files/LanguageSnippetTests/input/errors/mappingTypeCheckError4.pkl
vendored
Normal file
6
pkl-core/src/test/files/LanguageSnippetTests/input/errors/mappingTypeCheckError4.pkl
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
num = 1
|
||||
|
||||
// EntriesLiteralNode
|
||||
res: Mapping<String, String> = new {
|
||||
[num] = "foo"
|
||||
}
|
||||
7
pkl-core/src/test/files/LanguageSnippetTests/input/errors/mappingTypeCheckError5.pkl
vendored
Normal file
7
pkl-core/src/test/files/LanguageSnippetTests/input/errors/mappingTypeCheckError5.pkl
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
// GeneratorObjectLiteralNode
|
||||
res: Mapping<String, String> = new {
|
||||
when (false) {
|
||||
["foo"] = "foo"
|
||||
}
|
||||
[1] = "foo"
|
||||
}
|
||||
11
pkl-core/src/test/files/LanguageSnippetTests/input/errors/mappingTypeCheckError6.pkl
vendored
Normal file
11
pkl-core/src/test/files/LanguageSnippetTests/input/errors/mappingTypeCheckError6.pkl
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
a = new Listing { "hi" }
|
||||
|
||||
b = (a) {
|
||||
"hihih"
|
||||
}
|
||||
|
||||
bar = new Mapping {
|
||||
[b] = "foo"
|
||||
}
|
||||
|
||||
res: Mapping<Listing<String(length == 5)>, String> = bar
|
||||
3
pkl-core/src/test/files/LanguageSnippetTests/input/errors/mappingTypeCheckError7.pkl
vendored
Normal file
3
pkl-core/src/test/files/LanguageSnippetTests/input/errors/mappingTypeCheckError7.pkl
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
res = new Mapping<Listing<String(length == 5)>, String> {
|
||||
[new Listing { "hi" }] = "hi"
|
||||
}
|
||||
3
pkl-core/src/test/files/LanguageSnippetTests/input/errors/mappingTypeCheckError8.pkl
vendored
Normal file
3
pkl-core/src/test/files/LanguageSnippetTests/input/errors/mappingTypeCheckError8.pkl
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
res: Mapping<String, String> = new {
|
||||
...Map("foo", 1)
|
||||
}
|
||||
32
pkl-core/src/test/files/LanguageSnippetTests/input/listings/listing4.pkl
vendored
Normal file
32
pkl-core/src/test/files/LanguageSnippetTests/input/listings/listing4.pkl
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
amends "../snippetTest.pkl"
|
||||
|
||||
examples {
|
||||
["listings are lazy"] {
|
||||
// backed by ConstantEntriesLiteralNode
|
||||
local listing = new Listing<String> {
|
||||
"foo"
|
||||
throw("uh oh")
|
||||
}
|
||||
listing[0]
|
||||
module.catch(() -> listing[1])
|
||||
}
|
||||
["listings are lazy with generator entries"] {
|
||||
local listing = new Listing<String> {
|
||||
when (false) {
|
||||
"uh oh"
|
||||
}
|
||||
"foo"
|
||||
throw("uh oh")
|
||||
}
|
||||
listing[0]
|
||||
}
|
||||
["nested listings are also lazy"] {
|
||||
local listing = new Listing<Listing<String>> {
|
||||
new {
|
||||
"bar"
|
||||
throw("uh oh")
|
||||
}
|
||||
}
|
||||
listing[0][0]
|
||||
}
|
||||
}
|
||||
168
pkl-core/src/test/files/LanguageSnippetTests/input/listings/listing5.pkl
vendored
Normal file
168
pkl-core/src/test/files/LanguageSnippetTests/input/listings/listing5.pkl
vendored
Normal file
@@ -0,0 +1,168 @@
|
||||
amends "../snippetTest.pkl"
|
||||
|
||||
facts {
|
||||
["equals"] {
|
||||
local l1 = new Listing<String(length.isOdd)> {}
|
||||
local l2: Listing<String(this == capitalize())> = l1
|
||||
|
||||
l1 == l2
|
||||
}
|
||||
}
|
||||
|
||||
examples {
|
||||
["type check: new with explicit parent"] {
|
||||
local l = new Listing<String> {
|
||||
1
|
||||
}
|
||||
|
||||
module.catch(() -> l[0])
|
||||
}
|
||||
|
||||
["type check: local new with inferred parent"] {
|
||||
local l: Listing<String> = new {
|
||||
1
|
||||
}
|
||||
|
||||
module.catch(() -> l[0])
|
||||
}
|
||||
|
||||
["type check: local paramaterized property type, unparamaterized new with explicit parent"] {
|
||||
local m: Listing<String> = new Listing {
|
||||
1
|
||||
}
|
||||
module.catch(() -> m[0])
|
||||
}
|
||||
|
||||
["type check: local unparameterized property type, paramaterized new with explicit parent"] {
|
||||
local m: Listing = new Listing<String> {
|
||||
1
|
||||
}
|
||||
module.catch(() -> m[0])
|
||||
}
|
||||
|
||||
["amending listings does not require type checks on amending object members"] {
|
||||
local m: Listing<String> = new {
|
||||
"hi"
|
||||
}
|
||||
// ElementsLiteralNode
|
||||
(m) {
|
||||
1
|
||||
}
|
||||
// ElementsEntriesLiteralNode
|
||||
(m) {
|
||||
[0] = 1
|
||||
2
|
||||
}
|
||||
// GeneratorObjectLiteralNode
|
||||
(m) {
|
||||
when (false) {
|
||||
"hi"
|
||||
}
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
["type check: constraints on both property type node and explicit parent type node are checked"] {
|
||||
local l: Listing<String(length.isOdd)> = new Listing<String(this == capitalize())> {
|
||||
"Ba"
|
||||
"bar"
|
||||
}
|
||||
|
||||
module.catch(() -> l[0])
|
||||
module.catch(() -> l[1])
|
||||
}
|
||||
|
||||
["type check: nested listings: constraints on both parent type node and child type node are checked"] {
|
||||
local res12: Listing<Listing<String(length.isOdd)>> =
|
||||
new Listing<Listing<String(this == capitalize())>> {
|
||||
new {
|
||||
"Ba"
|
||||
"bar"
|
||||
}
|
||||
}
|
||||
|
||||
module.catch(() -> res12[0][0])
|
||||
module.catch(() -> res12[0][1])
|
||||
}
|
||||
|
||||
["type check: propagate from List"] {
|
||||
local l: List<Listing<String(length.isOdd)>> = List(
|
||||
new Listing<String(this == capitalize())> {
|
||||
"Ba"
|
||||
"bar"
|
||||
}
|
||||
)
|
||||
module.catch(() -> l[0][0])
|
||||
module.catch(() -> l[0][1])
|
||||
}
|
||||
|
||||
["type check: propagate function types"] {
|
||||
local l = new Listing<String(this == capitalize())> {
|
||||
"Ba" // fails `length.isOdd`
|
||||
"bar" // fails `this == capitalize()`
|
||||
}
|
||||
local l2 = new Listing {
|
||||
"Ba" // fails `length.isOdd`
|
||||
"bar" // fails `this == capitalize()`
|
||||
}
|
||||
// type check String(length.isOdd) should be propagated to the listing via a paramater type
|
||||
// annotation
|
||||
local function func1(listing: Listing<String(length.isOdd)>) = listing
|
||||
// type check String(length.isOdd) should be propagated to the listing via a return type
|
||||
// annotation
|
||||
local function func2(listing): Listing<String(length.isOdd)> = listing
|
||||
// type check String(length.isOdd) and String(this == capitalize()) should be propagated to the
|
||||
// listing via both parameter type and return type annotations
|
||||
local function func3(listing: Listing<String(length.isOdd)>): Listing<String(this == capitalize())> = listing
|
||||
|
||||
module.catch(() -> func1(l)[0])
|
||||
module.catch(() -> func1(l)[1])
|
||||
module.catch(() -> func2(l)[0])
|
||||
module.catch(() -> func2(l)[1])
|
||||
module.catch(() -> func3(l2)[0])
|
||||
module.catch(() -> func3(l2)[1])
|
||||
}
|
||||
|
||||
["type check: union type"] {
|
||||
local l: Listing<String(length.isOdd)>|Listing<String(length == 4)> =
|
||||
new Listing<String(this == capitalize())> {
|
||||
"Ba" // fails length.isOdd and length == 4
|
||||
"bar" // fails this == capitalize()
|
||||
"Bazz" // passes this == capitalize() and length == 4
|
||||
"Qux" // passes this == capitalize() and length.isOdd
|
||||
}
|
||||
module.catch(() -> l)
|
||||
}
|
||||
|
||||
["type check: nullable type"] {
|
||||
local l: Listing<String(length.isOdd)>? =
|
||||
new Listing<String(this == capitalize())> {
|
||||
"Ba" // fails length.isOdd
|
||||
"bar" // fails this == capitalize()
|
||||
}
|
||||
module.catch(() -> l!![0])
|
||||
module.catch(() -> l!![1])
|
||||
}
|
||||
|
||||
["type check: propagate lambda type"] {
|
||||
local func1 = (it: Listing<String(length.isOdd)>) -> it
|
||||
|
||||
local l = new Listing<String(this == capitalize())> {
|
||||
"Ba" // fails `length.isOdd`
|
||||
"bar" // fails `this == capitalize()`
|
||||
}
|
||||
|
||||
module.catch(() -> func1.apply(l)[0])
|
||||
module.catch(() -> func1.apply(l)[1])
|
||||
}
|
||||
|
||||
["intermediary objects are not checked"] {
|
||||
local l = new Listing<String> {
|
||||
// okay, because this node never gets evaluated
|
||||
50
|
||||
}
|
||||
(l) {
|
||||
[0] = "Hello"
|
||||
}
|
||||
}
|
||||
}
|
||||
46
pkl-core/src/test/files/LanguageSnippetTests/input/listings/listing6.pkl
vendored
Normal file
46
pkl-core/src/test/files/LanguageSnippetTests/input/listings/listing6.pkl
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
amends "../snippetTest.pkl"
|
||||
|
||||
local class TheClass {
|
||||
local isLongString = (it) -> it.length > 10
|
||||
|
||||
prop: Listing<String(isLongString)>
|
||||
}
|
||||
|
||||
local class TheClass2 {
|
||||
hidden requiredLength: Int
|
||||
|
||||
prop: Listing<String(length > requiredLength)>
|
||||
}
|
||||
|
||||
examples {
|
||||
["name resolution in type constraint"] {
|
||||
// should be able to resolve `isLongString` when checking this member
|
||||
module.catch(() -> new TheClass {
|
||||
prop {
|
||||
"too short"
|
||||
}
|
||||
}.prop[0])
|
||||
|
||||
new TheClass {
|
||||
prop {
|
||||
"this is long enough"
|
||||
}
|
||||
}
|
||||
}
|
||||
["resolves the receiver"] {
|
||||
local base: TheClass2 = new {
|
||||
requiredLength = 5
|
||||
}
|
||||
(base) {
|
||||
prop {
|
||||
"long enough"
|
||||
}
|
||||
}
|
||||
module.catch(() -> (base) {
|
||||
requiredLength = 10
|
||||
prop {
|
||||
"too short"
|
||||
}
|
||||
}.prop[0])
|
||||
}
|
||||
}
|
||||
10
pkl-core/src/test/files/LanguageSnippetTests/input/listings/listing7.pkl
vendored
Normal file
10
pkl-core/src/test/files/LanguageSnippetTests/input/listings/listing7.pkl
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// ensure that these members are only evaluated once (trace should only be emitted once)
|
||||
listing = new Listing { trace(1) }
|
||||
|
||||
listing2: Listing<Int> = listing
|
||||
|
||||
listing3 = new Listing {
|
||||
new Listing { trace(2) }
|
||||
}
|
||||
|
||||
listing4: Listing<Listing<Int>> = listing3
|
||||
@@ -1,41 +1,367 @@
|
||||
import "pkl:test"
|
||||
|
||||
hidden x1: Listing<String> = new {
|
||||
local x1: Listing<String> = new {
|
||||
"pigeon"
|
||||
42
|
||||
"barn owl"
|
||||
}
|
||||
|
||||
hidden x2: Listing<String(length > 3)> = new {
|
||||
local x2: Listing<String(length > 3)> = new {
|
||||
"pigeon"
|
||||
"bob"
|
||||
}
|
||||
|
||||
hidden x3: Listing<String>(!isEmpty)
|
||||
res1 = x1[0]
|
||||
res2 = test.catchOrNull(() -> x1[1])
|
||||
res3 = x2[0]
|
||||
res4 = test.catchOrNull(() -> x2[1])
|
||||
|
||||
res1 = test.catch(() -> x1)
|
||||
res2 = test.catch(() -> x2)
|
||||
res3 = test.catch(() -> x3)
|
||||
|
||||
hidden x4: Listing = new {
|
||||
local x4: Listing = new {
|
||||
throw("element unnecessarily evaluated")
|
||||
}
|
||||
|
||||
hidden x5: Listing<Any> = new {
|
||||
local x5: Listing<Any> = new {
|
||||
throw("element unnecessarily evaluated")
|
||||
}
|
||||
|
||||
hidden x6: Listing<unknown> = new {
|
||||
local x6: Listing<unknown> = new {
|
||||
throw("element unnecessarily evaluated")
|
||||
}
|
||||
|
||||
hidden x7 = new Listing {
|
||||
local x7 = new Listing {
|
||||
throw("element unnecessarily evaluated")
|
||||
42
|
||||
throw("element unnecessarily evaluated")
|
||||
}
|
||||
|
||||
res4 = x4.length == 1
|
||||
res5 = x5.length == 1
|
||||
res6 = x6.length == 1
|
||||
res7 = x7[1] == 42
|
||||
local x8 = new Listing<String> {
|
||||
throw("element unneccessarily evaluated")
|
||||
}
|
||||
|
||||
res5 = x4.length == 1
|
||||
res6 = x5.length == 1
|
||||
res7 = x6.length == 1
|
||||
res8 = x7[1] == 42
|
||||
res9 = x8.length == 1
|
||||
|
||||
local x9 = new Listing {
|
||||
"foo"
|
||||
1
|
||||
}
|
||||
local x10 = x9 as Listing<String>
|
||||
|
||||
res10 = x9 is Listing<String>
|
||||
res11 = x10[0]
|
||||
res12 = test.catch(() -> x10[1])
|
||||
|
||||
local x11: Listing<String(!isEmpty)> = new Listing<String> {
|
||||
""
|
||||
}
|
||||
|
||||
res13 = test.catch(() -> x11[0])
|
||||
|
||||
local x12: Listing<String> = new Listing<String(!isEmpty)> {
|
||||
""
|
||||
}
|
||||
|
||||
res14 = test.catch(() -> x12[0])
|
||||
|
||||
local l = new Listing { "foo"; 1 }
|
||||
|
||||
local x13: (Listing<String>|Listing<Int>) = l
|
||||
|
||||
local x14: Listing<String>|Listing<Int>? = l
|
||||
|
||||
local x15: Listing<String>|(Listing<Int>|Int) = l
|
||||
|
||||
local x16: Listing<String>|Int = l
|
||||
|
||||
res15 = test.catch(() -> x13)
|
||||
res16 = test.catch(() -> x14)
|
||||
res17 = test.catch(() -> x15)
|
||||
// just accessing x16 doesn't throw because only one Listing in the union type
|
||||
res18 = x16.length
|
||||
// noinspection TypeMismatch
|
||||
res19 = test.catch(() -> x16[1])
|
||||
|
||||
local x17: Listing<Listing<String>> = new {
|
||||
new {
|
||||
5
|
||||
}
|
||||
}
|
||||
|
||||
res20 = x17.length
|
||||
res21 = x17[0].length
|
||||
res22 = test.catch(() -> x17[0][0])
|
||||
|
||||
local x18 = new Listing { 1; 2; 3 } as Listing<String>
|
||||
|
||||
res23 = x18.length
|
||||
res24 = test.catch(() -> x18[0])
|
||||
|
||||
local x19 = new Listing<String> {
|
||||
when (true) {
|
||||
15
|
||||
}
|
||||
}
|
||||
|
||||
res25 = x19.length
|
||||
res26 = test.catch(() -> x19[0])
|
||||
|
||||
local x20 = new Listing<String> {
|
||||
...List(1, 2, 3)
|
||||
}
|
||||
|
||||
res27 = x20.length
|
||||
res28 = test.catch(() -> x20[0])
|
||||
|
||||
local x21 = new Listing<String> {
|
||||
for (elem in List(1, 2, 3)) {
|
||||
elem
|
||||
}
|
||||
}
|
||||
|
||||
res29 = x21.length
|
||||
res30 = test.catch(() -> x21[0])
|
||||
|
||||
local x22: Listing<String> = new {
|
||||
"hi"
|
||||
}
|
||||
|
||||
// typechecks not required when amending
|
||||
// ElementsLiteralNode
|
||||
res31 = (x22) {
|
||||
"hi"
|
||||
}
|
||||
|
||||
// ElementsEntriesLiteralNode
|
||||
res32 = (x22) {
|
||||
[0] = 1
|
||||
2
|
||||
}
|
||||
|
||||
// GeneratorObjectLiteralNode
|
||||
res33 = (x22) {
|
||||
when (false) {
|
||||
"hi"
|
||||
}
|
||||
1
|
||||
}
|
||||
|
||||
// GeneratorSpreadNode
|
||||
res34 = (x22) {
|
||||
...List(1, 2, 3)
|
||||
}
|
||||
|
||||
local x23: Listing<Listing<String(length.isOdd)>> =
|
||||
new Listing<Listing<String(this == capitalize())>> {
|
||||
new {
|
||||
"Ba"
|
||||
"bar"
|
||||
}
|
||||
}
|
||||
|
||||
res35 = test.catch(() -> x23[0][0])
|
||||
res36 = test.catch(() -> x23[0][1])
|
||||
|
||||
// check listings from inside a list
|
||||
local x24: List<Listing<String(length.isOdd)>> = List(
|
||||
new Listing<String(this == capitalize())> {
|
||||
"Ba"
|
||||
"bar"
|
||||
}
|
||||
)
|
||||
|
||||
res37 = test.catch(() -> x24[0][0])
|
||||
res38 = test.catch(() -> x24[0][1])
|
||||
|
||||
local x25: List<String|Listing<Int>> = List(
|
||||
"hello",
|
||||
new Listing {
|
||||
"foo"
|
||||
},
|
||||
"goodbye"
|
||||
)
|
||||
|
||||
res39 = x25[0]
|
||||
// retain lazy typecheck of listing.
|
||||
res40 = x25[1].length
|
||||
res41 = test.catch(() -> x25[1][0])
|
||||
res42 = x25[2]
|
||||
|
||||
// check listings from inside a set
|
||||
local x26: Set<Listing<String(length.isOdd)>> = Set(
|
||||
new Listing<String(this == capitalize())> {
|
||||
"Ba"
|
||||
"bar"
|
||||
}
|
||||
)
|
||||
|
||||
res43 = test.catch(() -> x26[0][0])
|
||||
|
||||
local x27: Set<String|Listing<Int>> = Set(
|
||||
"hello",
|
||||
new Listing {
|
||||
"foo"
|
||||
},
|
||||
"goodbye"
|
||||
)
|
||||
|
||||
// sets are eagerly checked (need to compute hash code, therefore need to deep force)
|
||||
res45 = test.catch(() -> x27)
|
||||
|
||||
local x28: List<Listing<Int>>|List<Listing<String>> = List(
|
||||
new Listing { "hello" }
|
||||
)
|
||||
|
||||
res46 = x28[0][0]
|
||||
|
||||
local x29: List<Listing<Int>>|List<Listing<String>> = List(
|
||||
new Listing { 1; "hello" }
|
||||
)
|
||||
|
||||
res47 = test.catch(() -> x29)
|
||||
|
||||
// check listings from inside a map
|
||||
local x30: Map<String, Listing<String(length.isOdd)>> = Map(
|
||||
"hello",
|
||||
new Listing<String(this == capitalize())> {
|
||||
"Ba"
|
||||
"bar"
|
||||
}
|
||||
)
|
||||
|
||||
res48 = x30["hello"].length
|
||||
res49 = test.catch(() -> x30["hello"][0])
|
||||
res50 = test.catch(() -> x30["hello"][1])
|
||||
|
||||
local x31: Map<String, Int|Listing<String>> = Map(
|
||||
"hello", 1,
|
||||
"thelisting", new Listing {
|
||||
1
|
||||
2
|
||||
},
|
||||
"goodbye", 2
|
||||
)
|
||||
|
||||
res51 = x31.length
|
||||
res52 = x31["hello"]
|
||||
res53 = x31["goodbye"]
|
||||
res54 = x31["thelisting"].length
|
||||
res55 = test.catch(() -> x31["thelisting"][0])
|
||||
res56 = test.catch(() -> x31["thelisting"][1])
|
||||
|
||||
local x32: Map<Listing<String>, Int> = Map(
|
||||
new Listing { 1; 2 },
|
||||
1
|
||||
)
|
||||
|
||||
res57 = test.catch(() -> x32)
|
||||
|
||||
local x33: Map<String, Listing<Int>|Int> = Map(
|
||||
"first", 1,
|
||||
"second", new Listing { "hi" }
|
||||
)
|
||||
|
||||
res58 = x33.length
|
||||
res59 = x33["first"]
|
||||
res60 = x33["second"].length
|
||||
res61 = test.catch(() -> x33["second"][0])
|
||||
|
||||
local x34: Pair<Listing<String>, Listing<String>> = Pair(
|
||||
new Listing { 1 },
|
||||
new Listing { 2 }
|
||||
)
|
||||
|
||||
res62 = x34.first.length
|
||||
res63 = x34.second.length
|
||||
|
||||
res64 = test.catch(() -> x34.first[0])
|
||||
res65 = test.catch(() -> x34.second[0])
|
||||
|
||||
local x35: Pair<Int, Listing<String>> = Pair(
|
||||
5,
|
||||
new Listing { 1 }
|
||||
)
|
||||
|
||||
res66 = x35.first
|
||||
res67 = x35.second.length
|
||||
res68 = test.catch(() -> x35.second[0])
|
||||
|
||||
local x36: Collection<Int|Listing<String>> = List(
|
||||
1,
|
||||
new Listing { "hello"; 1 }
|
||||
)
|
||||
|
||||
res69 = x36.length
|
||||
res70 = x36.first
|
||||
res71 = x36[1].length
|
||||
res73 = x36[1][0]
|
||||
res74 = test.catch(() -> x36[1][1])
|
||||
|
||||
local x37: Collection<Int|Listing<String>> = Set(
|
||||
1,
|
||||
new Listing {
|
||||
"hello"
|
||||
1
|
||||
}
|
||||
)
|
||||
|
||||
res75 = test.catch(() -> x37)
|
||||
|
||||
local x38: Collection<Listing<String>>|Collection<Listing<Int>> =
|
||||
List(new Listing {
|
||||
1
|
||||
"hi"
|
||||
})
|
||||
|
||||
res76 = test.catch(() -> x38)
|
||||
|
||||
local class Person {
|
||||
prop1 = 1
|
||||
prop2 = 2
|
||||
prop3 = "hi"
|
||||
}
|
||||
|
||||
local x39: Listing<Int> = new Person {}.toMap().values.toListing()
|
||||
|
||||
res77 = x39.length
|
||||
res78 = x39[0]
|
||||
res79 = x39[1]
|
||||
res80 = test.catch(() -> x39[2])
|
||||
|
||||
local x40: Listing<Int> = new {
|
||||
...List(1, 2, "hello")
|
||||
}
|
||||
|
||||
res81 = x40.length
|
||||
res82 = x40[0]
|
||||
res83 = x40[1]
|
||||
res84 = test.catch(() -> x40[2])
|
||||
|
||||
// returns a new listing
|
||||
function myFunction(elem: Listing<Int>) = elem
|
||||
|
||||
local x41 = myFunction(new Listing { "hello" })
|
||||
|
||||
res85 = x41.length
|
||||
res86 = test.catch(() -> x41[0])
|
||||
|
||||
function myFunction2(elem): Listing<Int> = elem
|
||||
|
||||
local x42 = myFunction(new Listing { "hello" })
|
||||
|
||||
res87 = x42.length
|
||||
res88 = test.catch(() -> x42[0])
|
||||
|
||||
local x43 = (it: Listing<Int>) -> it
|
||||
local x44 = x43.apply(new Listing { "hello" })
|
||||
|
||||
res89 = x44.length
|
||||
res90 = test.catch(() -> x44[0])
|
||||
|
||||
function myFunction3(elem: Listing<Int>): Listing<Int> = elem
|
||||
local x45 = myFunction3(new Listing { "hello" })
|
||||
|
||||
res91 = x45.length
|
||||
res92 = test.catch(() -> x45[0])
|
||||
|
||||
@@ -15,9 +15,9 @@ hidden x2: Listing<String(length > 3)> = new {
|
||||
|
||||
hidden x3: Listing<String>(!isEmpty)
|
||||
|
||||
res1 = test.catch(() -> x1)
|
||||
res2 = test.catch(() -> x2)
|
||||
res3 = test.catch(() -> x3)
|
||||
res1 = test.catch(() -> x1.toList())
|
||||
res2 = test.catch(() -> x2.toList())
|
||||
res3 = test.catch(() -> x3.toList())
|
||||
|
||||
hidden x4: Listing = new {
|
||||
when (true) {
|
||||
|
||||
@@ -27,12 +27,16 @@ local x6: Mapping<String, String(!isEmpty)> = new {
|
||||
["pigeon"] = ""
|
||||
}
|
||||
|
||||
res1 = test.catch(() -> x1)
|
||||
res2 = test.catch(() -> x2)
|
||||
res3 = test.catch(() -> x3)
|
||||
res4 = test.catch(() -> x4)
|
||||
res5 = test.catch(() -> x5)
|
||||
res6 = test.catch(() -> x6)
|
||||
res1 = x1.length
|
||||
res2 = test.catch(() -> x1["barn owl"])
|
||||
res3 = x2.length
|
||||
res4 = x2["fred"]
|
||||
res5 = test.catch(() -> x2["barney"])
|
||||
res6 = test.catch(() -> x3)
|
||||
res7 = test.catch(() -> x4)
|
||||
res8 = test.catch(() -> x5)
|
||||
res9 = x6.length
|
||||
res10 = test.catch(() -> x6["pigeon"])
|
||||
|
||||
hidden x7: Mapping = new {
|
||||
["first"] = throw("value unnecessarily evaluated")
|
||||
@@ -52,7 +56,312 @@ hidden x10 = new Mapping {
|
||||
["third"] = throw("value unnecessarily evaluated")
|
||||
}
|
||||
|
||||
res7 = !x7.isEmpty
|
||||
res8 = !x8.isEmpty
|
||||
res9 = !x9.isEmpty
|
||||
res10 = x10["second"] == 42
|
||||
res11 = x7.length
|
||||
res12 = x8.length
|
||||
res13 = x9.length
|
||||
res14 = x10.length
|
||||
res15 = x10["second"]
|
||||
|
||||
local x11: Mapping<String, String(!isEmpty)> = new Mapping<String, String> {
|
||||
["foo"] = ""
|
||||
}
|
||||
|
||||
res16 = x11.length
|
||||
res17 = test.catch(() -> x11["foo"])
|
||||
|
||||
local x12: Mapping<String, String> = new Mapping<String, String(!isEmpty)> {
|
||||
["foo"] = ""
|
||||
}
|
||||
|
||||
res18 = x12.length
|
||||
res19 = test.catch(() -> x12["foo"])
|
||||
|
||||
local m = new Mapping {
|
||||
["one"] = 1
|
||||
["two"] = "two"
|
||||
}
|
||||
|
||||
local x13: Mapping<String, String>|Mapping<String, Int> = m
|
||||
local x14: Mapping<String, String>|Mapping<String, Int>? = m
|
||||
local x15: Mapping<String, String>|(Mapping<String, Int>|Int) = m
|
||||
local x16: Mapping<String, String>|Int = m
|
||||
|
||||
res20 = test.catch(() -> x13)
|
||||
res21 = test.catch(() -> x14)
|
||||
res22 = test.catch(() -> x15)
|
||||
res23 = x16.length
|
||||
res24 = test.catch(() -> x16["one"])
|
||||
|
||||
local x17: Mapping<String, Mapping<String, String>> = new {
|
||||
["foo"] {
|
||||
["bar"] = 1
|
||||
}
|
||||
}
|
||||
|
||||
res25 = x17.length
|
||||
res26 = x17["foo"].length
|
||||
res27 = test.catch(() -> x17["foo"]["bar"])
|
||||
|
||||
local x18 = new Mapping { ["foo"] = 1 } as Mapping<String, String>
|
||||
|
||||
res28 = x18.length
|
||||
res29 = test.catch(() -> x18["foo"])
|
||||
|
||||
local x19 = new Mapping<String, String> {
|
||||
when (true) {
|
||||
["foo"] = 1
|
||||
}
|
||||
}
|
||||
|
||||
res30 = x19.length
|
||||
res31 = test.catch(() -> x19["foo"])
|
||||
|
||||
local x20 = new Mapping<String, String> {
|
||||
...Map("foo", 1)
|
||||
}
|
||||
|
||||
res32 = x20.length
|
||||
res33 = test.catch(() -> x20["foo"])
|
||||
|
||||
local x21 = new Mapping<String, String> {
|
||||
for (k, v in Map("foo", 1)) {
|
||||
[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
res34 = x21.length
|
||||
res35 = test.catch(() -> x21["foo"])
|
||||
|
||||
local x22: Mapping<String, String> = new {
|
||||
["hi"] = "hi"
|
||||
}
|
||||
|
||||
// typechecks not required when amending
|
||||
// ElementsEntriesLiteralNode
|
||||
res36 = (x22) {
|
||||
["hi2"] = 1
|
||||
}
|
||||
|
||||
// GeneratorObjectLiteralNode
|
||||
res37 = (x22) {
|
||||
when (true) {
|
||||
["hi2"] = 1
|
||||
}
|
||||
}
|
||||
|
||||
// GeneratorSpreadNode
|
||||
res38 = (x22) {
|
||||
...Map("hi2", 1)
|
||||
}
|
||||
|
||||
local x23: Mapping<String, Mapping<String, String(length.isOdd)>> = new Mapping<String, Mapping<String, String(this == capitalize())>> {
|
||||
["foo"] {
|
||||
["first"] = "Ba"
|
||||
["second"] = "bar"
|
||||
}
|
||||
}
|
||||
|
||||
res39 = test.catch(() -> x23["foo"]["first"])
|
||||
res40 = test.catch(() -> x23[""])
|
||||
|
||||
// check mappings from inside a list
|
||||
local x24: List<Mapping<String, String(length.isOdd)>> = List(
|
||||
new Mapping<String, String(this == capitalize())> {
|
||||
["first"] = "Ba"
|
||||
["second"] = "bar"
|
||||
}
|
||||
)
|
||||
|
||||
res41 = test.catch(() -> x24[0]["first"])
|
||||
res42 = test.catch(() -> x24[0]["second"])
|
||||
|
||||
local x25: List<String|Mapping<String, Int>> = List(
|
||||
"hello",
|
||||
new Mapping {
|
||||
["foo"] = "foo"
|
||||
},
|
||||
"goodbye"
|
||||
)
|
||||
|
||||
res43 = x25[0]
|
||||
// retain lazy typecheck of mapping.
|
||||
res44 = x25[1].length
|
||||
res45 = test.catch(() -> x25[1]["foo"])
|
||||
res46 = x25[2]
|
||||
|
||||
// check mappings from inside a set
|
||||
local x26: Set<Mapping<String, String(length.isOdd)>> = Set(
|
||||
new Mapping<String, String(this == capitalize())> {
|
||||
["first"] = "Ba"
|
||||
["second"] = "bar"
|
||||
}
|
||||
)
|
||||
|
||||
// sets are eagerly checked (need to compute hash code, therefore need to deep force)
|
||||
res47 = test.catch(() -> x26)
|
||||
|
||||
local x28: List<Mapping<String, Int>>|List<Mapping<String, String>> = List(
|
||||
new Mapping { ["foo"] = 1 }
|
||||
)
|
||||
|
||||
res48 = x28[0]["foo"]
|
||||
|
||||
local x29: List<Mapping<String, Int>>|List<Mapping<String, String>> = List(
|
||||
new Mapping {
|
||||
["foo"] = 1
|
||||
["bar"] = "hi"
|
||||
}
|
||||
)
|
||||
|
||||
res49 = test.catch(() -> x29)
|
||||
|
||||
// check mappings from inside a map
|
||||
local x30: Map<String, Mapping<String, String(length.isOdd)>> = Map(
|
||||
"hello",
|
||||
new Mapping<String, String(this == capitalize())> {
|
||||
["first"] = "Ba"
|
||||
["second"] = "bar"
|
||||
}
|
||||
)
|
||||
|
||||
res50 = x30["hello"].length
|
||||
res51 = test.catch(() -> x30["hello"]["first"])
|
||||
res52 = test.catch(() -> x30["hello"]["second"])
|
||||
|
||||
local x31: Map<String, Int|Mapping<String, String>> = Map(
|
||||
"hello", 1,
|
||||
"themapping", new Mapping {
|
||||
["one"] = 1
|
||||
["two"] = 2
|
||||
},
|
||||
"goodbye", 2
|
||||
)
|
||||
|
||||
res53 = x31.length
|
||||
res54 = x31["hello"]
|
||||
res55 = x31["goodbye"]
|
||||
res56 = x31["themapping"].length
|
||||
res57 = test.catch(() -> x31["themapping"]["one"])
|
||||
res58 = test.catch(() -> x31["themapping"]["two"])
|
||||
|
||||
local x32: Map<Mapping<String, String>, Int> = Map(
|
||||
new Mapping { ["one"] = 1 },
|
||||
1
|
||||
)
|
||||
|
||||
res59 = test.catch(() -> x32)
|
||||
|
||||
local x33: Map<String, Mapping<String, Int>|Int> = Map(
|
||||
"first", 1,
|
||||
"second", new Mapping { ["hi"] = "hi" }
|
||||
)
|
||||
|
||||
res60 = x33.length
|
||||
res61 = x33["first"]
|
||||
res62 = x33["second"].length
|
||||
res63 = test.catch(() -> x33["second"]["hi"])
|
||||
|
||||
local x34: Pair<Mapping<String, String>, Mapping<String, String>> = Pair(
|
||||
new Mapping { ["one"] = 1 },
|
||||
new Mapping { ["one"] = 1 }
|
||||
)
|
||||
|
||||
res64 = x34.first.length
|
||||
res65 = x34.second.length
|
||||
|
||||
res66 = test.catch(() -> x34.first["one"])
|
||||
res67 = test.catch(() -> x34.second["one"])
|
||||
|
||||
local x35: Pair<Int, Mapping<String, String>> = Pair(
|
||||
5,
|
||||
new Mapping { ["one"] = 1 }
|
||||
)
|
||||
|
||||
res68 = x35.first
|
||||
res69 = x35.second.length
|
||||
res70 = test.catch(() -> x35.second["one"])
|
||||
|
||||
local x36: Collection<Int|Mapping<String, String>> = List(
|
||||
1,
|
||||
new Mapping {
|
||||
["one"] = "hello"
|
||||
["two"] = 1
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
res71 = x36.length
|
||||
res72 = x36.first
|
||||
res73 = x36[1].length
|
||||
res74 = x36[1]["one"]
|
||||
res75 = test.catch(() -> x36[1]["two"])
|
||||
|
||||
local x37: Collection<Int|Mapping<String, String>> = Set(
|
||||
1,
|
||||
new Mapping {
|
||||
["one"] = "hello"
|
||||
["two"] = 1
|
||||
}
|
||||
)
|
||||
|
||||
res77 = test.catch(() -> x37)
|
||||
|
||||
local x38: Collection<Mapping<String, String>>|Collection<Mapping<String, Int>> =
|
||||
List(new Mapping {
|
||||
["one"] = "hello"
|
||||
["two"] = 1
|
||||
})
|
||||
|
||||
res78 = test.catch(() -> x38)
|
||||
|
||||
local class Person {
|
||||
prop1 = 1
|
||||
prop2 = 2
|
||||
prop3 = "hi"
|
||||
}
|
||||
|
||||
local x39: Mapping<String, Int> = new Person {}
|
||||
.toMap()
|
||||
.toMapping()
|
||||
|
||||
|
||||
res79 = x39.length
|
||||
res80 = x39["prop1"]
|
||||
res81 = x39["prop2"]
|
||||
res82 = test.catch(() -> x39["prop3"])
|
||||
|
||||
local x40: Mapping<String, Int> = new {
|
||||
...Map("one", 1, "two", 2, "three", "hello")
|
||||
}
|
||||
|
||||
res83 = x40.length
|
||||
res84 = x40["one"]
|
||||
res85 = x40["two"]
|
||||
res86 = test.catch(() -> x40["three"])
|
||||
|
||||
// returns a new mapping
|
||||
function myFunction(elem: Mapping<String, Int>) = elem
|
||||
|
||||
local x41 = myFunction(new Mapping { ["hello"] = "hello" })
|
||||
|
||||
res87 = x41.length
|
||||
res88 = test.catch(() -> x41["hello"])
|
||||
|
||||
function myFunction2(elem): Mapping<String, Int> = elem
|
||||
|
||||
local x42 = myFunction(new Mapping { ["hello"] = "hello" })
|
||||
|
||||
res89 = x42.length
|
||||
res90 = test.catch(() -> x42["hello"])
|
||||
|
||||
local x43 = (it: Mapping<String, Int>) -> it
|
||||
local x44 = x43.apply(new Mapping { ["hello"] = "hello" })
|
||||
|
||||
res91 = x44.length
|
||||
res92 = test.catch(() -> x44["hello"])
|
||||
|
||||
function myFunction3(elem: Mapping<String, Int>): Mapping<String, Int> = elem
|
||||
local x45 = myFunction3(new Mapping { ["hello"] = "hello" })
|
||||
|
||||
res93 = x45.length
|
||||
res94 = test.catch(() -> x45["hello"])
|
||||
|
||||
@@ -33,12 +33,12 @@ local x6: Mapping<String, String(!isEmpty)> = new {
|
||||
["pigeon"] = ""
|
||||
}
|
||||
|
||||
res1 = test.catch(() -> x1)
|
||||
res2 = test.catch(() -> x2)
|
||||
res1 = test.catch(() -> x1["barn owl"])
|
||||
res2 = test.catch(() -> x2["barney"])
|
||||
res3 = test.catch(() -> x3)
|
||||
res4 = test.catch(() -> x4)
|
||||
res5 = test.catch(() -> x5)
|
||||
res6 = test.catch(() -> x6)
|
||||
res6 = test.catch(() -> x6["pigeon"])
|
||||
|
||||
hidden x7: Mapping = new {
|
||||
when (true) {
|
||||
|
||||
17
pkl-core/src/test/files/LanguageSnippetTests/output/basic/as3.pcf
vendored
Normal file
17
pkl-core/src/test/files/LanguageSnippetTests/output/basic/as3.pcf
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
examples {
|
||||
["set of listing"] {
|
||||
1
|
||||
false
|
||||
"Expected value of type `Int`, but got type `String`. Value: \"one\""
|
||||
}
|
||||
["listing"] {
|
||||
true
|
||||
"Expected value of type `String`, but got type `Int`. Value: 1"
|
||||
}
|
||||
["mapping"] {
|
||||
true
|
||||
"Expected value of type `String`, but got type `Int`. Value: 1"
|
||||
"Expected value of type `String`, but got type `Int`. Value: 2"
|
||||
"Expected value of type `Int`, but got type `String`. Value: \"hi\""
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,6 @@ x | ["\(idx)_2"] = o // at this point, `o` should be out of scope
|
||||
^
|
||||
at forGeneratorWrongVariableName#res[#2] (file:///$snippetsDir/input/errors/forGeneratorWrongVariableName.pkl)
|
||||
|
||||
x | res: Mapping<String, Int> = new {
|
||||
^^^^^
|
||||
at forGeneratorWrongVariableName#res (file:///$snippetsDir/input/errors/forGeneratorWrongVariableName.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
15
pkl-core/src/test/files/LanguageSnippetTests/output/errors/listingTypeCheckError1.err
vendored
Normal file
15
pkl-core/src/test/files/LanguageSnippetTests/output/errors/listingTypeCheckError1.err
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
–– Pkl Error ––
|
||||
Expected value of type `String`, but got type `Int`.
|
||||
Value: 1
|
||||
|
||||
x | res = new Listing<String> {
|
||||
^^^^^^
|
||||
at listingTypeCheckError1#res (file:///$snippetsDir/input/errors/listingTypeCheckError1.pkl)
|
||||
|
||||
x | 1
|
||||
^
|
||||
at listingTypeCheckError1#res[#1] (file:///$snippetsDir/input/errors/listingTypeCheckError1.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
15
pkl-core/src/test/files/LanguageSnippetTests/output/errors/listingTypeCheckError2.err
vendored
Normal file
15
pkl-core/src/test/files/LanguageSnippetTests/output/errors/listingTypeCheckError2.err
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
–– Pkl Error ––
|
||||
Expected value of type `String`, but got type `Int`.
|
||||
Value: 1
|
||||
|
||||
x | res = new Listing<String> {
|
||||
^^^^^^
|
||||
at listingTypeCheckError2#res (file:///$snippetsDir/input/errors/listingTypeCheckError2.pkl)
|
||||
|
||||
x | one
|
||||
^^^
|
||||
at listingTypeCheckError2#res[#1] (file:///$snippetsDir/input/errors/listingTypeCheckError2.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
15
pkl-core/src/test/files/LanguageSnippetTests/output/errors/listingTypeCheckError3.err
vendored
Normal file
15
pkl-core/src/test/files/LanguageSnippetTests/output/errors/listingTypeCheckError3.err
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `!isEmpty` violated.
|
||||
Value: ""
|
||||
|
||||
x | res: Listing<String(!isEmpty)> = new Listing<String> {
|
||||
^^^^^^^^
|
||||
at listingTypeCheckError3#res (file:///$snippetsDir/input/errors/listingTypeCheckError3.pkl)
|
||||
|
||||
x | ""
|
||||
^^
|
||||
at listingTypeCheckError3#res[#1] (file:///$snippetsDir/input/errors/listingTypeCheckError3.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
15
pkl-core/src/test/files/LanguageSnippetTests/output/errors/listingTypeCheckError4.err
vendored
Normal file
15
pkl-core/src/test/files/LanguageSnippetTests/output/errors/listingTypeCheckError4.err
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `endsWith("ga")` violated.
|
||||
Value: "hola"
|
||||
|
||||
x | res: Listing<String(!isEmpty)> = new Listing<String(endsWith("ga"))> {
|
||||
^^^^^^^^^^^^^^
|
||||
at listingTypeCheckError4#res (file:///$snippetsDir/input/errors/listingTypeCheckError4.pkl)
|
||||
|
||||
x | "hola"
|
||||
^^^^^^
|
||||
at listingTypeCheckError4#res[#1] (file:///$snippetsDir/input/errors/listingTypeCheckError4.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
19
pkl-core/src/test/files/LanguageSnippetTests/output/errors/listingTypeCheckError5.err
vendored
Normal file
19
pkl-core/src/test/files/LanguageSnippetTests/output/errors/listingTypeCheckError5.err
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
–– Pkl Error ––
|
||||
Expected value of type `String`, but got type `Int`.
|
||||
Value: 1
|
||||
|
||||
x | local l = new Listing { 1; 2; 3 } as Listing<String>
|
||||
^^^^^^
|
||||
at listingTypeCheckError5#l (file:///$snippetsDir/input/errors/listingTypeCheckError5.pkl)
|
||||
|
||||
x | local l = new Listing { 1; 2; 3 } as Listing<String>
|
||||
^
|
||||
at listingTypeCheckError5#l[#1] (file:///$snippetsDir/input/errors/listingTypeCheckError5.pkl)
|
||||
|
||||
x | res = l[0]
|
||||
^^^^
|
||||
at listingTypeCheckError5#res (file:///$snippetsDir/input/errors/listingTypeCheckError5.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
15
pkl-core/src/test/files/LanguageSnippetTests/output/errors/mappingTypeCheckError1.err
vendored
Normal file
15
pkl-core/src/test/files/LanguageSnippetTests/output/errors/mappingTypeCheckError1.err
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
–– Pkl Error ––
|
||||
Expected value of type `String`, but got type `Int`.
|
||||
Value: 1
|
||||
|
||||
x | res = new Mapping<String, String> {
|
||||
^^^^^^
|
||||
at mappingTypeCheckError1#res (file:///$snippetsDir/input/errors/mappingTypeCheckError1.pkl)
|
||||
|
||||
x | ["foo"] = 1
|
||||
^
|
||||
at mappingTypeCheckError1#res["foo"] (file:///$snippetsDir/input/errors/mappingTypeCheckError1.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
15
pkl-core/src/test/files/LanguageSnippetTests/output/errors/mappingTypeCheckError2.err
vendored
Normal file
15
pkl-core/src/test/files/LanguageSnippetTests/output/errors/mappingTypeCheckError2.err
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
–– Pkl Error ––
|
||||
Expected value of type `String`, but got type `Int`.
|
||||
Value: 1
|
||||
|
||||
x | res: Mapping<String, String> = new {
|
||||
^^^^^^
|
||||
at mappingTypeCheckError2#res (file:///$snippetsDir/input/errors/mappingTypeCheckError2.pkl)
|
||||
|
||||
x | ["foo"] = 1
|
||||
^
|
||||
at mappingTypeCheckError2#res["foo"] (file:///$snippetsDir/input/errors/mappingTypeCheckError2.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
19
pkl-core/src/test/files/LanguageSnippetTests/output/errors/mappingTypeCheckError3.err
vendored
Normal file
19
pkl-core/src/test/files/LanguageSnippetTests/output/errors/mappingTypeCheckError3.err
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
–– Pkl Error ––
|
||||
Expected value of type `String`, but got type `Int`.
|
||||
Value: 1
|
||||
|
||||
x | res: Mapping<String, String> = new {
|
||||
^^^^^^
|
||||
at mappingTypeCheckError3#res (file:///$snippetsDir/input/errors/mappingTypeCheckError3.pkl)
|
||||
|
||||
x | [1] = "foo"
|
||||
^
|
||||
at mappingTypeCheckError3#res[1] (file:///$snippetsDir/input/errors/mappingTypeCheckError3.pkl)
|
||||
|
||||
x | res: Mapping<String, String> = new {
|
||||
^^^^^
|
||||
at mappingTypeCheckError3#res (file:///$snippetsDir/input/errors/mappingTypeCheckError3.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
19
pkl-core/src/test/files/LanguageSnippetTests/output/errors/mappingTypeCheckError4.err
vendored
Normal file
19
pkl-core/src/test/files/LanguageSnippetTests/output/errors/mappingTypeCheckError4.err
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
–– Pkl Error ––
|
||||
Expected value of type `String`, but got type `Int`.
|
||||
Value: 1
|
||||
|
||||
x | res: Mapping<String, String> = new {
|
||||
^^^^^^
|
||||
at mappingTypeCheckError4#res (file:///$snippetsDir/input/errors/mappingTypeCheckError4.pkl)
|
||||
|
||||
x | [num] = "foo"
|
||||
^^^
|
||||
at mappingTypeCheckError4#res[#1] (file:///$snippetsDir/input/errors/mappingTypeCheckError4.pkl)
|
||||
|
||||
x | res: Mapping<String, String> = new {
|
||||
^^^^^
|
||||
at mappingTypeCheckError4#res (file:///$snippetsDir/input/errors/mappingTypeCheckError4.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
19
pkl-core/src/test/files/LanguageSnippetTests/output/errors/mappingTypeCheckError5.err
vendored
Normal file
19
pkl-core/src/test/files/LanguageSnippetTests/output/errors/mappingTypeCheckError5.err
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
–– Pkl Error ––
|
||||
Expected value of type `String`, but got type `Int`.
|
||||
Value: 1
|
||||
|
||||
x | res: Mapping<String, String> = new {
|
||||
^^^^^^
|
||||
at mappingTypeCheckError5#res (file:///$snippetsDir/input/errors/mappingTypeCheckError5.pkl)
|
||||
|
||||
x | [1] = "foo"
|
||||
^
|
||||
at mappingTypeCheckError5#res[1] (file:///$snippetsDir/input/errors/mappingTypeCheckError5.pkl)
|
||||
|
||||
x | res: Mapping<String, String> = new {
|
||||
^^^^^
|
||||
at mappingTypeCheckError5#res (file:///$snippetsDir/input/errors/mappingTypeCheckError5.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
19
pkl-core/src/test/files/LanguageSnippetTests/output/errors/mappingTypeCheckError6.err
vendored
Normal file
19
pkl-core/src/test/files/LanguageSnippetTests/output/errors/mappingTypeCheckError6.err
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `length == 5` violated.
|
||||
Value: "hi"
|
||||
|
||||
xx | res: Mapping<Listing<String(length == 5)>, String> = bar
|
||||
^^^^^^^^^^^
|
||||
at mappingTypeCheckError6#res (file:///$snippetsDir/input/errors/mappingTypeCheckError6.pkl)
|
||||
|
||||
x | [b] = "foo"
|
||||
^
|
||||
at mappingTypeCheckError6#bar[#1] (file:///$snippetsDir/input/errors/mappingTypeCheckError6.pkl)
|
||||
|
||||
xx | res: Mapping<Listing<String(length == 5)>, String> = bar
|
||||
^^^
|
||||
at mappingTypeCheckError6#res (file:///$snippetsDir/input/errors/mappingTypeCheckError6.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
15
pkl-core/src/test/files/LanguageSnippetTests/output/errors/mappingTypeCheckError7.err
vendored
Normal file
15
pkl-core/src/test/files/LanguageSnippetTests/output/errors/mappingTypeCheckError7.err
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `length == 5` violated.
|
||||
Value: "hi"
|
||||
|
||||
x | res = new Mapping<Listing<String(length == 5)>, String> {
|
||||
^^^^^^^^^^^
|
||||
at mappingTypeCheckError7#res (file:///$snippetsDir/input/errors/mappingTypeCheckError7.pkl)
|
||||
|
||||
x | [new Listing { "hi" }] = "hi"
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
at mappingTypeCheckError7#res[#1] (file:///$snippetsDir/input/errors/mappingTypeCheckError7.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
11
pkl-core/src/test/files/LanguageSnippetTests/output/errors/mappingTypeCheckError8.err
vendored
Normal file
11
pkl-core/src/test/files/LanguageSnippetTests/output/errors/mappingTypeCheckError8.err
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
–– Pkl Error ––
|
||||
Expected value of type `String`, but got type `Int`.
|
||||
Value: 1
|
||||
|
||||
x | res: Mapping<String, String> = new {
|
||||
^^^^^^
|
||||
at mappingTypeCheckError8#res (file:///$snippetsDir/input/errors/mappingTypeCheckError8.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
12
pkl-core/src/test/files/LanguageSnippetTests/output/listings/listing4.pcf
vendored
Normal file
12
pkl-core/src/test/files/LanguageSnippetTests/output/listings/listing4.pcf
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
examples {
|
||||
["listings are lazy"] {
|
||||
"foo"
|
||||
"uh oh"
|
||||
}
|
||||
["listings are lazy with generator entries"] {
|
||||
"foo"
|
||||
}
|
||||
["nested listings are also lazy"] {
|
||||
"bar"
|
||||
}
|
||||
}
|
||||
69
pkl-core/src/test/files/LanguageSnippetTests/output/listings/listing5.pcf
vendored
Normal file
69
pkl-core/src/test/files/LanguageSnippetTests/output/listings/listing5.pcf
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
facts {
|
||||
["equals"] {
|
||||
true
|
||||
}
|
||||
}
|
||||
examples {
|
||||
["type check: new with explicit parent"] {
|
||||
"Expected value of type `String`, but got type `Int`. Value: 1"
|
||||
}
|
||||
["type check: local new with inferred parent"] {
|
||||
"Expected value of type `String`, but got type `Int`. Value: 1"
|
||||
}
|
||||
["type check: local paramaterized property type, unparamaterized new with explicit parent"] {
|
||||
"Expected value of type `String`, but got type `Int`. Value: 1"
|
||||
}
|
||||
["type check: local unparameterized property type, paramaterized new with explicit parent"] {
|
||||
"Expected value of type `String`, but got type `Int`. Value: 1"
|
||||
}
|
||||
["amending listings does not require type checks on amending object members"] {
|
||||
new {
|
||||
"hi"
|
||||
1
|
||||
}
|
||||
new {
|
||||
1
|
||||
2
|
||||
}
|
||||
new {
|
||||
"hi"
|
||||
1
|
||||
}
|
||||
}
|
||||
["type check: constraints on both property type node and explicit parent type node are checked"] {
|
||||
"Type constraint `length.isOdd` violated. Value: \"Ba\""
|
||||
"Type constraint `this == capitalize()` violated. Value: \"bar\""
|
||||
}
|
||||
["type check: nested listings: constraints on both parent type node and child type node are checked"] {
|
||||
"Type constraint `length.isOdd` violated. Value: \"Ba\""
|
||||
"Type constraint `this == capitalize()` violated. Value: \"bar\""
|
||||
}
|
||||
["type check: propagate from List"] {
|
||||
"Type constraint `length.isOdd` violated. Value: \"Ba\""
|
||||
"Type constraint `this == capitalize()` violated. Value: \"bar\""
|
||||
}
|
||||
["type check: propagate function types"] {
|
||||
"Type constraint `length.isOdd` violated. Value: \"Ba\""
|
||||
"Type constraint `this == capitalize()` violated. Value: \"bar\""
|
||||
"Type constraint `length.isOdd` violated. Value: \"Ba\""
|
||||
"Type constraint `this == capitalize()` violated. Value: \"bar\""
|
||||
"Type constraint `length.isOdd` violated. Value: \"Ba\""
|
||||
"Type constraint `this == capitalize()` violated. Value: \"bar\""
|
||||
}
|
||||
["type check: union type"] {
|
||||
"Expected value of type `Listing<String(length.isOdd)>|Listing<String(length == 4)>`, but got a different `Listing`. Value: new Listing { \"Ba\"; ?; ?; ? }"
|
||||
}
|
||||
["type check: nullable type"] {
|
||||
"Type constraint `length.isOdd` violated. Value: \"Ba\""
|
||||
"Type constraint `this == capitalize()` violated. Value: \"bar\""
|
||||
}
|
||||
["type check: propagate lambda type"] {
|
||||
"Type constraint `length.isOdd` violated. Value: \"Ba\""
|
||||
"Type constraint `this == capitalize()` violated. Value: \"bar\""
|
||||
}
|
||||
["intermediary objects are not checked"] {
|
||||
new {
|
||||
"Hello"
|
||||
}
|
||||
}
|
||||
}
|
||||
18
pkl-core/src/test/files/LanguageSnippetTests/output/listings/listing6.pcf
vendored
Normal file
18
pkl-core/src/test/files/LanguageSnippetTests/output/listings/listing6.pcf
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
examples {
|
||||
["name resolution in type constraint"] {
|
||||
"Type constraint `isLongString` violated. Value: \"too short\""
|
||||
new {
|
||||
prop {
|
||||
"this is long enough"
|
||||
}
|
||||
}
|
||||
}
|
||||
["resolves the receiver"] {
|
||||
new {
|
||||
prop {
|
||||
"long enough"
|
||||
}
|
||||
}
|
||||
"Type constraint `length > requiredLength` violated. Value: \"too short\""
|
||||
}
|
||||
}
|
||||
18
pkl-core/src/test/files/LanguageSnippetTests/output/listings/listing7.err
vendored
Normal file
18
pkl-core/src/test/files/LanguageSnippetTests/output/listings/listing7.err
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
listing {
|
||||
1
|
||||
}
|
||||
listing2 {
|
||||
1
|
||||
}
|
||||
listing3 {
|
||||
new {
|
||||
2
|
||||
}
|
||||
}
|
||||
listing4 {
|
||||
new {
|
||||
2
|
||||
}
|
||||
}
|
||||
pkl: TRACE: 1 = 1 (file:///$snippetsDir/input/listings/listing7.pkl)
|
||||
pkl: TRACE: 2 = 2 (file:///$snippetsDir/input/listings/listing7.pkl)
|
||||
@@ -1,7 +1,104 @@
|
||||
res1 = "Expected value of type `String`, but got type `Int`. Value: 42"
|
||||
res2 = "Type constraint `length > 3` violated. Value: \"bob\""
|
||||
res3 = "Type constraint `!isEmpty` violated. Value: new Listing {}"
|
||||
res4 = true
|
||||
res1 = "pigeon"
|
||||
res2 = "Expected value of type `String`, but got type `Int`. Value: 42"
|
||||
res3 = "pigeon"
|
||||
res4 = "Type constraint `length > 3` violated. Value: \"bob\""
|
||||
res5 = true
|
||||
res6 = true
|
||||
res7 = true
|
||||
res8 = true
|
||||
res9 = true
|
||||
res10 = false
|
||||
res11 = "foo"
|
||||
res12 = "Expected value of type `String`, but got type `Int`. Value: 1"
|
||||
res13 = "Type constraint `!isEmpty` violated. Value: \"\""
|
||||
res14 = "Type constraint `!isEmpty` violated. Value: \"\""
|
||||
res15 = "Expected value of type `Listing<String>|Listing<Int>`, but got a different `Listing`. Value: new Listing { \"foo\"; 1 }"
|
||||
res16 = "Expected value of type `Listing<String>|Listing<Int>?`, but got a different `Listing`. Value: new Listing { \"foo\"; 1 }"
|
||||
res17 = "Expected value of type `Listing<String>|(Listing<Int>|Int)`, but got a different `Listing`. Value: new Listing { \"foo\"; 1 }"
|
||||
res18 = 2
|
||||
res19 = "Expected value of type `String`, but got type `Int`. Value: 1"
|
||||
res20 = 1
|
||||
res21 = 1
|
||||
res22 = "Expected value of type `String`, but got type `Int`. Value: 5"
|
||||
res23 = 3
|
||||
res24 = "Expected value of type `String`, but got type `Int`. Value: 1"
|
||||
res25 = 1
|
||||
res26 = "Expected value of type `String`, but got type `Int`. Value: 15"
|
||||
res27 = 3
|
||||
res28 = "Expected value of type `String`, but got type `Int`. Value: 1"
|
||||
res29 = 3
|
||||
res30 = "Expected value of type `String`, but got type `Int`. Value: 1"
|
||||
res31 {
|
||||
"hi"
|
||||
"hi"
|
||||
}
|
||||
res32 {
|
||||
1
|
||||
2
|
||||
}
|
||||
res33 {
|
||||
"hi"
|
||||
1
|
||||
}
|
||||
res34 {
|
||||
"hi"
|
||||
1
|
||||
2
|
||||
3
|
||||
}
|
||||
res35 = "Type constraint `length.isOdd` violated. Value: \"Ba\""
|
||||
res36 = "Type constraint `this == capitalize()` violated. Value: \"bar\""
|
||||
res37 = "Type constraint `length.isOdd` violated. Value: \"Ba\""
|
||||
res38 = "Type constraint `this == capitalize()` violated. Value: \"bar\""
|
||||
res39 = "hello"
|
||||
res40 = 1
|
||||
res41 = "Expected value of type `Int`, but got type `String`. Value: \"foo\""
|
||||
res42 = "goodbye"
|
||||
res43 = "Type constraint `this == capitalize()` violated. Value: \"bar\""
|
||||
res45 = "Expected value of type `String|Listing<Int>`, but got a different `Listing`. Value: new Listing { \"foo\" }"
|
||||
res46 = "hello"
|
||||
res47 = "Expected value of type `List<Listing<Int>>|List<Listing<String>>`, but got a different `List`. Value: List(new Listing { 1; \"hello\" })"
|
||||
res48 = 2
|
||||
res49 = "Type constraint `length.isOdd` violated. Value: \"Ba\""
|
||||
res50 = "Type constraint `this == capitalize()` violated. Value: \"bar\""
|
||||
res51 = 3
|
||||
res52 = 1
|
||||
res53 = 2
|
||||
res54 = 2
|
||||
res55 = "Expected value of type `String`, but got type `Int`. Value: 1"
|
||||
res56 = "Expected value of type `String`, but got type `Int`. Value: 2"
|
||||
res57 = "Expected value of type `String`, but got type `Int`. Value: 1"
|
||||
res58 = 2
|
||||
res59 = 1
|
||||
res60 = 1
|
||||
res61 = "Expected value of type `Int`, but got type `String`. Value: \"hi\""
|
||||
res62 = 1
|
||||
res63 = 1
|
||||
res64 = "Expected value of type `String`, but got type `Int`. Value: 1"
|
||||
res65 = "Expected value of type `String`, but got type `Int`. Value: 2"
|
||||
res66 = 5
|
||||
res67 = 1
|
||||
res68 = "Expected value of type `String`, but got type `Int`. Value: 1"
|
||||
res69 = 2
|
||||
res70 = 1
|
||||
res71 = 2
|
||||
res73 = "hello"
|
||||
res74 = "Expected value of type `String`, but got type `Int`. Value: 1"
|
||||
res75 = "Expected value of type `Int|Listing<String>`, but got a different `Listing`. Value: new Listing { \"hello\"; 1 }"
|
||||
res76 = "Expected value of type `Collection<Listing<String>>|Collection<Listing<Int>>`, but got a different `List`. Value: List(new Listing { 1; \"hi\" })"
|
||||
res77 = 3
|
||||
res78 = 1
|
||||
res79 = 2
|
||||
res80 = "Expected value of type `Int`, but got type `String`. Value: \"hi\""
|
||||
res81 = 3
|
||||
res82 = 1
|
||||
res83 = 2
|
||||
res84 = "Expected value of type `Int`, but got type `String`. Value: \"hello\""
|
||||
res85 = 1
|
||||
res86 = "Expected value of type `Int`, but got type `String`. Value: \"hello\""
|
||||
res87 = 1
|
||||
res88 = "Expected value of type `Int`, but got type `String`. Value: \"hello\""
|
||||
res89 = 1
|
||||
res90 = "Expected value of type `Int`, but got type `String`. Value: \"hello\""
|
||||
res91 = 1
|
||||
res92 = "Expected value of type `Int`, but got type `String`. Value: \"hello\""
|
||||
|
||||
@@ -1,10 +1,102 @@
|
||||
res1 = "Expected value of type `List`, but got type `Int`. Value: 42"
|
||||
res1 = 2
|
||||
res2 = "Expected value of type `List`, but got type `Int`. Value: 42"
|
||||
res3 = "Expected value of type `List`, but got type `Int`. Value: 42"
|
||||
res4 = "Expected value of type `List`, but got type `Int`. Value: 42"
|
||||
res5 = "Type constraint `!isEmpty` violated. Value: \"\""
|
||||
res6 = "Type constraint `!isEmpty` violated. Value: \"\""
|
||||
res7 = true
|
||||
res8 = true
|
||||
res9 = true
|
||||
res10 = true
|
||||
res3 = 2
|
||||
res4 = List(1, 2, 3)
|
||||
res5 = "Expected value of type `List`, but got type `Int`. Value: 42"
|
||||
res6 = "Expected value of type `List`, but got type `Int`. Value: 42"
|
||||
res7 = "Expected value of type `List`, but got type `Int`. Value: 42"
|
||||
res8 = "Type constraint `!isEmpty` violated. Value: \"\""
|
||||
res9 = 1
|
||||
res10 = "Type constraint `!isEmpty` violated. Value: \"\""
|
||||
res11 = 1
|
||||
res12 = 1
|
||||
res13 = 1
|
||||
res14 = 3
|
||||
res15 = 42
|
||||
res16 = 1
|
||||
res17 = "Type constraint `!isEmpty` violated. Value: \"\""
|
||||
res18 = 1
|
||||
res19 = "Type constraint `!isEmpty` violated. Value: \"\""
|
||||
res20 = "Expected value of type `Mapping<String, String>|Mapping<String, Int>`, but got a different `Mapping`. Value: new Mapping { [\"one\"] = 1; [\"two\"] = \"two\" }"
|
||||
res21 = "Expected value of type `Mapping<String, String>|Mapping<String, Int>?`, but got a different `Mapping`. Value: new Mapping { [\"one\"] = 1; [\"two\"] = \"two\" }"
|
||||
res22 = "Expected value of type `Mapping<String, String>|(Mapping<String, Int>|Int)`, but got a different `Mapping`. Value: new Mapping { [\"one\"] = 1; [\"two\"] = \"two\" }"
|
||||
res23 = 2
|
||||
res24 = "Expected value of type `String`, but got type `Int`. Value: 1"
|
||||
res25 = 1
|
||||
res26 = 1
|
||||
res27 = "Expected value of type `String`, but got type `Int`. Value: 1"
|
||||
res28 = 1
|
||||
res29 = "Expected value of type `String`, but got type `Int`. Value: 1"
|
||||
res30 = 1
|
||||
res31 = "Expected value of type `String`, but got type `Int`. Value: 1"
|
||||
res32 = 1
|
||||
res33 = "Expected value of type `String`, but got type `Int`. Value: 1"
|
||||
res34 = 1
|
||||
res35 = "Expected value of type `String`, but got type `Int`. Value: 1"
|
||||
res36 {
|
||||
["hi"] = "hi"
|
||||
["hi2"] = 1
|
||||
}
|
||||
res37 {
|
||||
["hi"] = "hi"
|
||||
["hi2"] = 1
|
||||
}
|
||||
res38 {
|
||||
["hi"] = "hi"
|
||||
["hi2"] = 1
|
||||
}
|
||||
res39 = "Type constraint `length.isOdd` violated. Value: \"Ba\""
|
||||
res40 = "Cannot find key `\"\"`."
|
||||
res41 = "Type constraint `length.isOdd` violated. Value: \"Ba\""
|
||||
res42 = "Type constraint `this == capitalize()` violated. Value: \"bar\""
|
||||
res43 = "hello"
|
||||
res44 = 1
|
||||
res45 = "Expected value of type `Int`, but got type `String`. Value: \"foo\""
|
||||
res46 = "goodbye"
|
||||
res47 = "Type constraint `this == capitalize()` violated. Value: \"bar\""
|
||||
res48 = 1
|
||||
res49 = "Expected value of type `List<Mapping<String, Int>>|List<Mapping<String, String>>`, but got a different `List`. Value: List(new Mapping { [\"foo\"] = 1; [\"bar\"] = \"hi\" })"
|
||||
res50 = 2
|
||||
res51 = "Type constraint `length.isOdd` violated. Value: \"Ba\""
|
||||
res52 = "Type constraint `this == capitalize()` violated. Value: \"bar\""
|
||||
res53 = 3
|
||||
res54 = 1
|
||||
res55 = 2
|
||||
res56 = 2
|
||||
res57 = "Expected value of type `String`, but got type `Int`. Value: 1"
|
||||
res58 = "Expected value of type `String`, but got type `Int`. Value: 2"
|
||||
res59 = "Expected value of type `String`, but got type `Int`. Value: 1"
|
||||
res60 = 2
|
||||
res61 = 1
|
||||
res62 = 1
|
||||
res63 = "Expected value of type `Int`, but got type `String`. Value: \"hi\""
|
||||
res64 = 1
|
||||
res65 = 1
|
||||
res66 = "Expected value of type `String`, but got type `Int`. Value: 1"
|
||||
res67 = "Expected value of type `String`, but got type `Int`. Value: 1"
|
||||
res68 = 5
|
||||
res69 = 1
|
||||
res70 = "Expected value of type `String`, but got type `Int`. Value: 1"
|
||||
res71 = 2
|
||||
res72 = 1
|
||||
res73 = 2
|
||||
res74 = "hello"
|
||||
res75 = "Expected value of type `String`, but got type `Int`. Value: 1"
|
||||
res77 = "Expected value of type `Int|Mapping<String, String>`, but got a different `Mapping`. Value: new Mapping { [\"one\"] = \"hello\"; [\"two\"] = 1 }"
|
||||
res78 = "Expected value of type `Collection<Mapping<String, String>>|Collection<Mapping<String, Int>>`, but got a different `List`. Value: List(new Mapping { [\"one\"] = \"hello\"; [\"two\"] = 1 })"
|
||||
res79 = 3
|
||||
res80 = 1
|
||||
res81 = 2
|
||||
res82 = "Expected value of type `Int`, but got type `String`. Value: \"hi\""
|
||||
res83 = 3
|
||||
res84 = 1
|
||||
res85 = 2
|
||||
res86 = "Expected value of type `Int`, but got type `String`. Value: \"hello\""
|
||||
res87 = 1
|
||||
res88 = "Expected value of type `Int`, but got type `String`. Value: \"hello\""
|
||||
res89 = 1
|
||||
res90 = "Expected value of type `Int`, but got type `String`. Value: \"hello\""
|
||||
res91 = 1
|
||||
res92 = "Expected value of type `Int`, but got type `String`. Value: \"hello\""
|
||||
res93 = 1
|
||||
res94 = "Expected value of type `Int`, but got type `String`. Value: \"hello\""
|
||||
|
||||
@@ -10,6 +10,6 @@ at pkl.Project#dependencies (pkl:Project)
|
||||
Type constraint `isValidLoadDependency` violated.
|
||||
Value: new ModuleClass { package = null; tests {}; dependencies {}; evaluatorSetti...
|
||||
|
||||
x | dependencies {
|
||||
^^^^^^^^^^^^^^
|
||||
at PklProject#dependencies (file:///$snippetsDir/input/projects/badProjectDeps4/PklProject)
|
||||
x | ["badLocalProject"] = import("../badLocalProject/PklProject")
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at PklProject#dependencies["badLocalProject"] (file:///$snippetsDir/input/projects/badProjectDeps4/PklProject)
|
||||
|
||||
@@ -114,13 +114,13 @@ class EvaluateTestsTest {
|
||||
)
|
||||
|
||||
assertThat(results.totalTests()).isEqualTo(1)
|
||||
assertThat(results.totalFailures()).isEqualTo(0)
|
||||
assertThat(results.totalFailures()).isEqualTo(1)
|
||||
assertThat(results.failed()).isTrue
|
||||
|
||||
val res = results.results[0]
|
||||
assertThat(res.name).isEqualTo("text")
|
||||
assertThat(res.failures).isEmpty()
|
||||
assertThat(res.errors.size).isEqualTo(1)
|
||||
assertThat(res.name).isEqualTo("should fail")
|
||||
assertThat(res.failures).hasSize(1)
|
||||
assertThat(res.errors).hasSize(1)
|
||||
|
||||
val error = res.errors[0]
|
||||
assertThat(error.message).isEqualTo("got an error")
|
||||
@@ -134,10 +134,6 @@ class EvaluateTestsTest {
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
at text#facts["should fail"][#2] (repl:text)
|
||||
|
||||
3 | facts {
|
||||
^^^^^^^
|
||||
at text#facts (repl:text)
|
||||
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user