Add VmType intermediate representation for types (#1838)
This commit is contained in:
1 parent
cbffd6c114
commit
7ed88e250b
36 files changed
+2162
-1478
No files matched your search
+2
-1
@@ -12,6 +12,7 @@
|
|||||||
<option name="REPORT_FIELDS" value="true" />
|
<option name="REPORT_FIELDS" value="true" />
|
||||||
</inspection_tool>
|
</inspection_tool>
|
||||||
<inspection_tool class="ClassCanBeRecord" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
<inspection_tool class="ClassCanBeRecord" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="ClassEscapesItsScope" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||||
<inspection_tool class="CustomRegExpInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
<inspection_tool class="CustomRegExpInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
<option name="myConfigurations">
|
<option name="myConfigurations">
|
||||||
<list>
|
<list>
|
||||||
@@ -107,4 +108,4 @@
|
|||||||
<inspection_tool class="StringToUpperWithoutLocale" enabled="true" level="WARNING" enabled_by_default="true" editorAttributes="WARNING_ATTRIBUTES" />
|
<inspection_tool class="StringToUpperWithoutLocale" enabled="true" level="WARNING" enabled_by_default="true" editorAttributes="WARNING_ATTRIBUTES" />
|
||||||
<inspection_tool class="dd497f47-d38f-3fab-9ed7-eabe699620c8" enabled="true" level="ERROR" enabled_by_default="true" editorAttributes="ERRORS_ATTRIBUTES" />
|
<inspection_tool class="dd497f47-d38f-3fab-9ed7-eabe699620c8" enabled="true" level="ERROR" enabled_by_default="true" editorAttributes="ERRORS_ATTRIBUTES" />
|
||||||
</profile>
|
</profile>
|
||||||
</component>
|
</component>
|
||||||
@@ -124,7 +124,7 @@ public abstract class SubscriptNode extends BinaryExpressionNode {
|
|||||||
CompilerDirectives.transferToInterpreter();
|
CompilerDirectives.transferToInterpreter();
|
||||||
throw exceptionBuilder()
|
throw exceptionBuilder()
|
||||||
.evalError(
|
.evalError(
|
||||||
"operatorNotDefined2", getShortName(), reference.exportType(), VmUtils.getClass(key))
|
"operatorNotDefined2", getShortName(), reference.getType(), VmUtils.getClass(key))
|
||||||
.withProgramValue("Left operand", reference)
|
.withProgramValue("Left operand", reference)
|
||||||
.withProgramValue("Right operand", key)
|
.withProgramValue("Right operand", key)
|
||||||
.withHint(getReferenceHint(reference, err, key))
|
.withHint(getReferenceHint(reference, err, key))
|
||||||
|
|||||||
+2
-2
@@ -41,9 +41,9 @@ public final class CheckIsAnnotationClassNode extends ExpressionNode {
|
|||||||
typeNode = insert(unresolvedTypeNode.execute(frame));
|
typeNode = insert(unresolvedTypeNode.execute(frame));
|
||||||
unresolvedTypeNode = null;
|
unresolvedTypeNode = null;
|
||||||
}
|
}
|
||||||
var clazz = typeNode.getVmClass();
|
var clazz = typeNode.getType().getVmClass();
|
||||||
if (clazz != null && clazz.isSubclassOf(BaseModule.getAnnotationClass())) {
|
if (clazz != null && clazz.isSubclassOf(BaseModule.getAnnotationClass())) {
|
||||||
return typeNode.getVmClass();
|
return clazz;
|
||||||
}
|
}
|
||||||
|
|
||||||
CompilerDirectives.transferToInterpreter();
|
CompilerDirectives.transferToInterpreter();
|
||||||
|
|||||||
+1
-1
@@ -57,7 +57,7 @@ public abstract class AbstractInferParentNode extends ExpressionNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// try to produce a more specific error message than "cannotInstantiateType"
|
// try to produce a more specific error message than "cannotInstantiateType"
|
||||||
var clazz = typeNode.getVmClass();
|
var clazz = typeNode.getType().getVmClass();
|
||||||
if (clazz != null) {
|
if (clazz != null) {
|
||||||
VmUtils.checkIsInstantiable(clazz, typeNode);
|
VmUtils.checkIsInstantiable(clazz, typeNode);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import com.oracle.truffle.api.nodes.IndirectCallNode;
|
|||||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||||
import com.oracle.truffle.api.source.SourceSection;
|
import com.oracle.truffle.api.source.SourceSection;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.pkl.core.PType;
|
|
||||||
import org.pkl.core.ast.ExpressionNode;
|
import org.pkl.core.ast.ExpressionNode;
|
||||||
import org.pkl.core.ast.MemberLookupMode;
|
import org.pkl.core.ast.MemberLookupMode;
|
||||||
import org.pkl.core.ast.member.ClassProperty;
|
import org.pkl.core.ast.member.ClassProperty;
|
||||||
@@ -77,7 +76,7 @@ public abstract class ReadPropertyNode extends ExpressionNode {
|
|||||||
case DEFAULT_MEMBER ->
|
case DEFAULT_MEMBER ->
|
||||||
ErrorMessages.create(
|
ErrorMessages.create(
|
||||||
"cannotReferenceDefaultProperty",
|
"cannotReferenceDefaultProperty",
|
||||||
((PType.Class) err.getType()).getPClass().getSimpleName());
|
((VmType.ClassType) err.getType()).getVmClass().getSimpleName());
|
||||||
case EXTERNAL_CLASS ->
|
case EXTERNAL_CLASS ->
|
||||||
ErrorMessages.create(
|
ErrorMessages.create(
|
||||||
"cannotReferencePropertyInExternalClass", propertyName, err.getType());
|
"cannotReferencePropertyInExternalClass", propertyName, err.getType());
|
||||||
@@ -92,7 +91,7 @@ public abstract class ReadPropertyNode extends ExpressionNode {
|
|||||||
} catch (VmReferenceAccessError err) {
|
} catch (VmReferenceAccessError err) {
|
||||||
CompilerDirectives.transferToInterpreter();
|
CompilerDirectives.transferToInterpreter();
|
||||||
throw exceptionBuilder()
|
throw exceptionBuilder()
|
||||||
.evalError("cannotFindPropertyInObjectNoHint", propertyName, receiver.exportType())
|
.evalError("cannotFindPropertyInObjectNoHint", propertyName, receiver.getType())
|
||||||
.withHint(getReferenceErrorHint(receiver, err))
|
.withHint(getReferenceErrorHint(receiver, err))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ public final class ClassNode extends ExpressionNode {
|
|||||||
try {
|
try {
|
||||||
if (unresolvedSupertypeNode != null) {
|
if (unresolvedSupertypeNode != null) {
|
||||||
var supertypeNode = unresolvedSupertypeNode.execute(frame);
|
var supertypeNode = unresolvedSupertypeNode.execute(frame);
|
||||||
var superclass = supertypeNode.getVmClass();
|
var superclass = supertypeNode.getType().getVmClass();
|
||||||
|
|
||||||
checkSupertype(supertypeNode, superclass);
|
checkSupertype(supertypeNode, superclass);
|
||||||
cachedClass.initSupertype(supertypeNode, superclass);
|
cachedClass.initSupertype(supertypeNode, superclass);
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import org.pkl.core.ast.ExpressionNode;
|
|||||||
import org.pkl.core.ast.VmModifier;
|
import org.pkl.core.ast.VmModifier;
|
||||||
import org.pkl.core.ast.type.TypeNode;
|
import org.pkl.core.ast.type.TypeNode;
|
||||||
import org.pkl.core.runtime.*;
|
import org.pkl.core.runtime.*;
|
||||||
|
import org.pkl.core.runtime.VmType.UnknownType;
|
||||||
import org.pkl.core.util.CollectionUtils;
|
import org.pkl.core.util.CollectionUtils;
|
||||||
import org.pkl.core.util.Pair;
|
import org.pkl.core.util.Pair;
|
||||||
|
|
||||||
@@ -86,6 +87,10 @@ public final class FunctionNode extends RegularMemberNode {
|
|||||||
return parameterTypeNodes[idx];
|
return parameterTypeNodes[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public VmType getReturnType() {
|
||||||
|
return returnTypeNode == null ? UnknownType.INSTANCE : returnTypeNode.getType();
|
||||||
|
}
|
||||||
|
|
||||||
public @Nullable TypeNode getReturnTypeNode() {
|
public @Nullable TypeNode getReturnTypeNode() {
|
||||||
return returnTypeNode;
|
return returnTypeNode;
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
@@ -198,13 +198,13 @@ public abstract class UnresolvedTypeNode extends PklNode {
|
|||||||
case "Int16":
|
case "Int16":
|
||||||
return new Int16TypeAliasTypeNode();
|
return new Int16TypeAliasTypeNode();
|
||||||
case "UInt16":
|
case "UInt16":
|
||||||
return new UIntTypeAliasTypeNode(alias, 0x000000000000FFFFL);
|
return new IntMaskSlotTypeNode(alias, 0x000000000000FFFFL);
|
||||||
case "Int32":
|
case "Int32":
|
||||||
return new Int32TypeAliasTypeNode();
|
return new Int32TypeAliasTypeNode();
|
||||||
case "UInt32":
|
case "UInt32":
|
||||||
return new UIntTypeAliasTypeNode(alias, 0x00000000FFFFFFFFL);
|
return new IntMaskSlotTypeNode(alias, 0x00000000FFFFFFFFL);
|
||||||
case "UInt":
|
case "UInt":
|
||||||
return new UIntTypeAliasTypeNode(alias, 0x7FFFFFFFFFFFFFFFL);
|
return new IntMaskSlotTypeNode(alias, 0x7FFFFFFFFFFFFFFFL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import com.oracle.truffle.api.source.SourceSection;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.pkl.core.PType;
|
|
||||||
import org.pkl.core.StackFrame;
|
import org.pkl.core.StackFrame;
|
||||||
import org.pkl.core.ValueFormatter;
|
import org.pkl.core.ValueFormatter;
|
||||||
import org.pkl.core.ast.type.TypeNode.UnionTypeNode;
|
import org.pkl.core.ast.type.TypeNode.UnionTypeNode;
|
||||||
@@ -95,6 +94,7 @@ public abstract class VmTypeMismatchException extends ControlFlowException {
|
|||||||
|
|
||||||
assert expectedType instanceof VmClass
|
assert expectedType instanceof VmClass
|
||||||
|| expectedType instanceof VmTypeAlias
|
|| expectedType instanceof VmTypeAlias
|
||||||
|
|| expectedType instanceof VmType
|
||||||
|| expectedType instanceof String // string literal type
|
|| expectedType instanceof String // string literal type
|
||||||
|| expectedType instanceof Set; // union of string literal types
|
|| expectedType instanceof Set; // union of string literal types
|
||||||
|
|
||||||
@@ -153,13 +153,11 @@ public abstract class VmTypeMismatchException extends ControlFlowException {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var valueType =
|
||||||
|
actualValue instanceof VmReference ref ? ref.getType() : VmUtils.getClass(actualValue);
|
||||||
|
|
||||||
builder
|
builder
|
||||||
.append(
|
.append(ErrorMessages.createIndented("typeMismatch", indent, renderedType, valueType))
|
||||||
ErrorMessages.createIndented(
|
|
||||||
"typeMismatch",
|
|
||||||
indent,
|
|
||||||
renderedType,
|
|
||||||
VmUtils.getClass(actualValue).toPklString()))
|
|
||||||
.append("\n")
|
.append("\n")
|
||||||
.append(indent)
|
.append(indent)
|
||||||
.append("Value: ")
|
.append("Value: ")
|
||||||
@@ -183,10 +181,10 @@ public abstract class VmTypeMismatchException extends ControlFlowException {
|
|||||||
renderedExpected = "Class<" + expectedClass.toPklString() + ">";
|
renderedExpected = "Class<" + expectedClass.toPklString() + ">";
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClassType(SourceSection sourceSection, VmClass actualClass, PType expectedType) {
|
public ClassType(SourceSection sourceSection, VmClass actualClass, VmType expectedType) {
|
||||||
super(sourceSection, actualClass);
|
super(sourceSection, actualClass);
|
||||||
this.expectedClass = null;
|
this.expectedClass = null;
|
||||||
renderedExpected = "Class<" + expectedType + ">";
|
renderedExpected = expectedType.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -400,44 +398,4 @@ public abstract class VmTypeMismatchException extends ControlFlowException {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class Reference extends VmTypeMismatchException {
|
|
||||||
|
|
||||||
private final PType expectedDomainType;
|
|
||||||
private final PType expectedReferentType;
|
|
||||||
|
|
||||||
public Reference(
|
|
||||||
SourceSection sourceSection,
|
|
||||||
VmReference actualValue,
|
|
||||||
PType expectedDomainType,
|
|
||||||
PType expectedReferentType) {
|
|
||||||
super(sourceSection, actualValue);
|
|
||||||
this.expectedDomainType = expectedDomainType;
|
|
||||||
this.expectedReferentType = expectedReferentType;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void buildMessage(
|
|
||||||
AnsiStringBuilder builder, String indent, boolean withPowerAssertions) {
|
|
||||||
builder
|
|
||||||
.append(
|
|
||||||
ErrorMessages.createIndented(
|
|
||||||
"typeMismatch",
|
|
||||||
indent,
|
|
||||||
new PType.Class(
|
|
||||||
RefModule.getReferenceClass().export(),
|
|
||||||
expectedDomainType,
|
|
||||||
expectedReferentType),
|
|
||||||
((VmReference) actualValue).exportType()))
|
|
||||||
.append("\n")
|
|
||||||
.append(indent)
|
|
||||||
.append("Value: ")
|
|
||||||
.append(VmValueRenderer.singleLine(80 - indent.length()).render(actualValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Boolean hasHint() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -25,6 +25,7 @@ import java.net.URI;
|
|||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@@ -175,12 +176,12 @@ public final class CommandSpecParser {
|
|||||||
}
|
}
|
||||||
if (!(optionsTypeNode instanceof TypeNode.UserClassTypeNode node)) {
|
if (!(optionsTypeNode instanceof TypeNode.UserClassTypeNode node)) {
|
||||||
throw exceptionBuilder()
|
throw exceptionBuilder()
|
||||||
.withSourceSection(optionsTypeNode.getSourceSection())
|
.withSourceSection(optionsPropertyTypeNode.getSourceSection())
|
||||||
.evalError(
|
.evalError(
|
||||||
"commandOptionsTypeNotClass", optionsTypeNode.getSourceSection().getCharacters())
|
"commandOptionsTypeNotClass", optionsTypeNode.getSourceSection().getCharacters())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
var clazz = node.getVmClass();
|
var clazz = ((VmType.ClassType) node.getType()).getVmClass();
|
||||||
if (clazz.isAbstract()) {
|
if (clazz.isAbstract()) {
|
||||||
throw exceptionBuilder()
|
throw exceptionBuilder()
|
||||||
.withSourceSection(clazz.getHeaderSection())
|
.withSourceSection(clazz.getHeaderSection())
|
||||||
@@ -308,14 +309,14 @@ public final class CommandSpecParser {
|
|||||||
|
|
||||||
// assert type is Boolean
|
// assert type is Boolean
|
||||||
var typeInfo = resolveType(prop);
|
var typeInfo = resolveType(prop);
|
||||||
if (!(typeInfo.getFirst() instanceof TypeNode.BooleanTypeNode)) {
|
if (typeInfo.getFirst().getVmClass() != BaseModule.getBooleanClass()) {
|
||||||
throw exceptionBuilder()
|
throw exceptionBuilder()
|
||||||
.withSourceSection(prop.getHeaderSection())
|
.withSourceSection(prop.getHeaderSection())
|
||||||
.evalError(
|
.evalError(
|
||||||
"commandFlagInvalidType",
|
"commandFlagInvalidType",
|
||||||
prop.getName(),
|
prop.getName(),
|
||||||
"BooleanFlag",
|
"BooleanFlag",
|
||||||
typeInfo.getFirst().getSourceSection().getCharacters(),
|
typeInfo.getFirst().toString(),
|
||||||
"Boolean")
|
"Boolean")
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
@@ -342,7 +343,7 @@ public final class CommandSpecParser {
|
|||||||
"commandFlagInvalidType",
|
"commandFlagInvalidType",
|
||||||
prop.getName(),
|
prop.getName(),
|
||||||
"CountedFlag",
|
"CountedFlag",
|
||||||
typeInfo.getFirst().getSourceSection().getCharacters(),
|
typeInfo.getFirst().toString(),
|
||||||
"Int")
|
"Int")
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
@@ -385,10 +386,10 @@ public final class CommandSpecParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Unwrap nullables, constraints, and aliases and return Pair(underlying type, is nullable) */
|
/** Unwrap nullables, constraints, and aliases and return Pair(underlying type, is nullable) */
|
||||||
private Pair<TypeNode, Boolean> resolveType(ClassProperty prop) {
|
private Pair<VmType, Boolean> resolveType(ClassProperty prop) {
|
||||||
var propertyTypeNode = prop.getTypeNode();
|
var propertyTypeNode = prop.getTypeNode();
|
||||||
if (propertyTypeNode != null) {
|
if (propertyTypeNode != null) {
|
||||||
return resolveType(propertyTypeNode.getTypeNode());
|
return resolveType(propertyTypeNode.getTypeNode().getType());
|
||||||
}
|
}
|
||||||
throw exceptionBuilder()
|
throw exceptionBuilder()
|
||||||
.withSourceSection(prop.getHeaderSection())
|
.withSourceSection(prop.getHeaderSection())
|
||||||
@@ -397,23 +398,23 @@ public final class CommandSpecParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Unwrap nullables, constraints, and aliases and return Pair(underlying type, is nullable) */
|
/** Unwrap nullables, constraints, and aliases and return Pair(underlying type, is nullable) */
|
||||||
private Pair<TypeNode, Boolean> resolveType(TypeNode typeNode) {
|
private Pair<VmType, Boolean> resolveType(VmType type) {
|
||||||
var isNullable = false;
|
var isNullable = false;
|
||||||
while (true) {
|
while (true) {
|
||||||
if (typeNode instanceof TypeNode.NullableTypeNode nullableTypeNode) {
|
if (type instanceof VmType.NullableType nullableType) {
|
||||||
isNullable = true;
|
isNullable = true;
|
||||||
typeNode = nullableTypeNode.getElementTypeNode();
|
type = nullableType.getElementType();
|
||||||
} else if (typeNode instanceof TypeNode.ConstrainedTypeNode constrainedTypeNode) {
|
} else if (type instanceof VmType.ConstrainedType constrainedType) {
|
||||||
typeNode = constrainedTypeNode.getChildTypeNode();
|
type = constrainedType.getBaseType();
|
||||||
} else if (typeNode instanceof TypeNode.TypeAliasTypeNode typeAliasTypeNode) {
|
} else if (type instanceof VmType.AliasType aliasType) {
|
||||||
if (typeAliasTypeNode.getVmTypeAlias() == BaseModule.getCharTypeAlias()) break;
|
if (aliasType.getVmTypeAlias() == BaseModule.getCharTypeAlias()) break;
|
||||||
typeNode = typeAliasTypeNode.getAliasedTypeNode();
|
type = aliasType.getAliasedType();
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Pair.of(typeNode, isNullable);
|
return Pair.of(type, isNullable);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This sigil used to indicate that an option has a default value that is a non-constant expr.
|
// This sigil used to indicate that an option has a default value that is a non-constant expr.
|
||||||
@@ -447,10 +448,12 @@ public final class CommandSpecParser {
|
|||||||
// wrapped by a nullable, no default value
|
// wrapped by a nullable, no default value
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (resolved.getFirst() instanceof TypeNode.UnionOfStringLiteralsTypeNode union
|
if (resolved.getFirst() instanceof VmType.UnionType union
|
||||||
&& union.getUnionDefault() != null) {
|
&& union.getDefaultIndex() != -1
|
||||||
return union.getUnionDefault();
|
&& union.isUnionOfStringLiterals()) {
|
||||||
} else if (resolved.getFirst() instanceof TypeNode.StringLiteralTypeNode literal) {
|
return ((VmType.StringLiteralType) union.getElementTypes()[union.getDefaultIndex()])
|
||||||
|
.getLiteral();
|
||||||
|
} else if (resolved.getFirst() instanceof VmType.StringLiteralType literal) {
|
||||||
return literal.getLiteral();
|
return literal.getLiteral();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -509,130 +512,151 @@ public final class CommandSpecParser {
|
|||||||
|
|
||||||
public OptionBehavior resolve(ClassProperty prop, boolean requireExplicitDefault) {
|
public OptionBehavior resolve(ClassProperty prop, boolean requireExplicitDefault) {
|
||||||
var resolved = resolveType(prop);
|
var resolved = resolveType(prop);
|
||||||
var typeNode = resolved.getFirst();
|
var type = resolved.getFirst();
|
||||||
isNullable = resolved.getSecond();
|
isNullable = resolved.getSecond();
|
||||||
defaultValue = CommandSpecParser.this.getDefaultValue(prop, requireExplicitDefault);
|
defaultValue = CommandSpecParser.this.getDefaultValue(prop, requireExplicitDefault);
|
||||||
|
|
||||||
resolve(prop, typeNode);
|
resolve(prop, type);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resolve(ClassProperty prop, TypeNode typeNode) {
|
private void resolve(ClassProperty prop, VmType type) {
|
||||||
if (resolvePrimitive(typeNode)) {
|
if (resolvePrimitive(type)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (typeNode instanceof TypeNode.ListingTypeNode listingTypeNode) {
|
if (type instanceof VmType.ClassType ct) {
|
||||||
handleElement(listingTypeNode.getValueTypeNode(), prop);
|
if (ct.getVmClass() == BaseModule.getListingClass()) {
|
||||||
if (multiple == null) multiple = true;
|
handleElement(prop, ct);
|
||||||
if (all == null)
|
if (multiple == null) {
|
||||||
all =
|
multiple = true;
|
||||||
!multiple
|
}
|
||||||
? this::allChooseLast
|
if (all == null) {
|
||||||
: (values, workingDirUri) -> {
|
all =
|
||||||
if (values.isEmpty()) return null;
|
!multiple
|
||||||
var builder = new VmObjectBuilder();
|
? this::allChooseLast
|
||||||
values.forEach(builder::addElement);
|
: (values, workingDirUri) -> {
|
||||||
return builder.toListing();
|
if (values.isEmpty()) return null;
|
||||||
};
|
var builder = new VmObjectBuilder();
|
||||||
} else if (typeNode instanceof TypeNode.MappingTypeNode mappingTypeNode) {
|
values.forEach(builder::addElement);
|
||||||
assert mappingTypeNode.getKeyTypeNode() != null;
|
return builder.toListing();
|
||||||
handleEntry(mappingTypeNode.getKeyTypeNode(), mappingTypeNode.getValueTypeNode(), prop);
|
};
|
||||||
if (multiple == null) multiple = true;
|
}
|
||||||
if (all == null)
|
} else if (ct.getVmClass() == BaseModule.getMappingClass()) {
|
||||||
all =
|
handleEntry(prop, ct);
|
||||||
!multiple
|
if (multiple == null) {
|
||||||
? this::allChooseLast
|
multiple = true;
|
||||||
: (values, workingDirUri) -> {
|
}
|
||||||
if (values.isEmpty()) return null;
|
if (all == null) {
|
||||||
var builder = new VmObjectBuilder();
|
all =
|
||||||
values.forEach(
|
!multiple
|
||||||
(entry) ->
|
? this::allChooseLast
|
||||||
builder.addEntry(
|
: (values, workingDirUri) -> {
|
||||||
((VmPair) entry).getFirst(), ((VmPair) entry).getSecond()));
|
if (values.isEmpty()) return null;
|
||||||
return builder.toMapping();
|
var builder = new VmObjectBuilder();
|
||||||
};
|
values.forEach(
|
||||||
} else if (typeNode instanceof TypeNode.ListTypeNode listTypeNode) {
|
(entry) ->
|
||||||
handleElement(listTypeNode.getElementTypeNode(), prop);
|
builder.addEntry(
|
||||||
if (multiple == null) multiple = true;
|
((VmPair) entry).getFirst(), ((VmPair) entry).getSecond()));
|
||||||
if (all == null)
|
return builder.toMapping();
|
||||||
all =
|
};
|
||||||
!multiple
|
}
|
||||||
? this::allChooseLast
|
} else if (ct.getVmClass() == BaseModule.getListClass()) {
|
||||||
: (values, workingDirUri) -> values.isEmpty() ? null : VmList.create(values);
|
handleElement(prop, ct);
|
||||||
} else if (typeNode instanceof TypeNode.SetTypeNode setTypeNode) {
|
if (multiple == null) {
|
||||||
handleElement(setTypeNode.getElementTypeNode(), prop);
|
multiple = true;
|
||||||
if (multiple == null) multiple = true;
|
}
|
||||||
if (all == null)
|
if (all == null) {
|
||||||
all =
|
all =
|
||||||
!multiple
|
!multiple
|
||||||
? this::allChooseLast
|
? this::allChooseLast
|
||||||
: (values, workingDirUri) -> values.isEmpty() ? null : VmSet.create(values);
|
: (values, workingDirUri) -> values.isEmpty() ? null : VmList.create(values);
|
||||||
} else if (typeNode instanceof TypeNode.MapTypeNode mapTypeNode) {
|
}
|
||||||
handleEntry(mapTypeNode.getKeyTypeNode(), mapTypeNode.getValueTypeNode(), prop);
|
} else if (ct.getVmClass() == BaseModule.getSetClass()) {
|
||||||
if (multiple == null) multiple = true;
|
handleElement(prop, ct);
|
||||||
if (all == null)
|
if (multiple == null) {
|
||||||
all =
|
multiple = true;
|
||||||
!multiple
|
}
|
||||||
? this::allChooseLast
|
if (all == null) {
|
||||||
: (values, workingDirUri) -> {
|
all =
|
||||||
if (values.isEmpty()) return null;
|
!multiple
|
||||||
var builder = VmMap.builder();
|
? this::allChooseLast
|
||||||
values.forEach(
|
: (values, workingDirUri) -> values.isEmpty() ? null : VmSet.create(values);
|
||||||
(entry) ->
|
}
|
||||||
builder.add(((VmPair) entry).getFirst(), ((VmPair) entry).getSecond()));
|
} else if (ct.getVmClass() == BaseModule.getMapClass()) {
|
||||||
return builder.build();
|
handleEntry(prop, ct);
|
||||||
};
|
if (multiple == null) {
|
||||||
} else if (typeNode instanceof TypeNode.PairTypeNode pairTypeNode) {
|
multiple = true;
|
||||||
handleEntry(pairTypeNode.getFirstTypeNode(), pairTypeNode.getSecondTypeNode(), prop);
|
}
|
||||||
if (all == null) all = this::allChooseLast;
|
if (all == null) {
|
||||||
if (multiple == null) multiple = false;
|
all =
|
||||||
} else if (typeNode instanceof TypeNode.FinalClassTypeNode finalClassTypeNode
|
!multiple
|
||||||
&& (finalClassTypeNode.getVmClass() == BaseModule.getListingClass()
|
? this::allChooseLast
|
||||||
|| finalClassTypeNode.getVmClass() == BaseModule.getMappingClass()
|
: (values, workingDirUri) -> {
|
||||||
|| finalClassTypeNode.getVmClass() == BaseModule.getListClass()
|
if (values.isEmpty()) return null;
|
||||||
|| finalClassTypeNode.getVmClass() == BaseModule.getSetClass()
|
var builder = VmMap.builder();
|
||||||
|| finalClassTypeNode.getVmClass() == BaseModule.getMapClass()
|
values.forEach(
|
||||||
|| finalClassTypeNode.getVmClass() == BaseModule.getPairClass())) {
|
(entry) ->
|
||||||
// if a supported type is provided without type arguments
|
builder.add(
|
||||||
throw exceptionBuilder()
|
((VmPair) entry).getFirst(), ((VmPair) entry).getSecond()));
|
||||||
.withSourceSection(prop.getHeaderSection())
|
return builder.build();
|
||||||
.evalError(
|
};
|
||||||
"commandOptionUnsupportedType",
|
}
|
||||||
prop.getName(),
|
} else if (ct.getVmClass() == BaseModule.getPairClass()) {
|
||||||
"",
|
handleEntry(prop, ct);
|
||||||
typeNode.getSourceSection().getCharacters())
|
if (all == null) {
|
||||||
.withHint(
|
all = this::allChooseLast;
|
||||||
finalClassTypeNode.getVmClass().getSimpleName()
|
}
|
||||||
+ " options must provide "
|
if (multiple == null) {
|
||||||
+ switch (finalClassTypeNode.getVmClass().getTypeParameterCount()) {
|
multiple = false;
|
||||||
case 1 -> "one type argument.";
|
}
|
||||||
case 2 -> "two type arguments.";
|
}
|
||||||
default -> throw PklBugException.unreachableCode();
|
}
|
||||||
})
|
|
||||||
.build();
|
if (each == null && all == null) {
|
||||||
} else if (each == null && all == null) {
|
|
||||||
// if another type and no transform functions are provided, that's an error
|
// if another type and no transform functions are provided, that's an error
|
||||||
throw exceptionBuilder()
|
throw unsupportedOptionNoTransform(prop, type);
|
||||||
.withSourceSection(prop.getHeaderSection())
|
|
||||||
.evalError(
|
|
||||||
"commandOptionUnsupportedType",
|
|
||||||
prop.getName(),
|
|
||||||
"",
|
|
||||||
typeNode.getSourceSection().getCharacters())
|
|
||||||
.withHint("Use a supported type or define a transformEach and/or transformAll function")
|
|
||||||
.build();
|
|
||||||
} else {
|
} else {
|
||||||
// if we have at least one transform then allow the type and fill in reasonable defaults
|
// if we have at least one transform then allow the type and fill in reasonable defaults
|
||||||
if (each == null) each = (rawValue, workingDirUri) -> rawValue;
|
if (each == null) {
|
||||||
if (all == null) all = this::allChooseLast;
|
each = (rawValue, workingDirUri) -> rawValue;
|
||||||
if (multiple == null) multiple = false;
|
}
|
||||||
if (metavar == null) metavar = METAVAR_VALUE;
|
if (all == null) {
|
||||||
|
all = this::allChooseLast;
|
||||||
|
}
|
||||||
|
if (multiple == null) {
|
||||||
|
multiple = false;
|
||||||
|
}
|
||||||
|
if (metavar == null) {
|
||||||
|
metavar = METAVAR_VALUE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private VmException unsupportedOptionTypeArguments(ClassProperty prop, VmType.ClassType type) {
|
||||||
|
var typeArgCountText =
|
||||||
|
switch (type.getVmClass().getTypeParameterCount()) {
|
||||||
|
case 1 -> "one type argument.";
|
||||||
|
case 2 -> "two type arguments.";
|
||||||
|
default -> throw PklBugException.unreachableCode();
|
||||||
|
};
|
||||||
|
return exceptionBuilder()
|
||||||
|
.withSourceSection(prop.getHeaderSection())
|
||||||
|
.evalError("commandOptionUnsupportedType", prop.getName(), "", type.toString())
|
||||||
|
.withHint(type.getVmClass().getSimpleName() + " options must provide " + typeArgCountText)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private VmException unsupportedOptionNoTransform(ClassProperty prop, VmType type) {
|
||||||
|
return exceptionBuilder()
|
||||||
|
.withSourceSection(prop.getHeaderSection())
|
||||||
|
.evalError("commandOptionUnsupportedType", prop.getName(), "", type.toString())
|
||||||
|
.withHint("Use a supported type or define a transformEach and/or transformAll function")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
private OptionBehavior resolveTypeArgument(
|
private OptionBehavior resolveTypeArgument(
|
||||||
ClassProperty prop, TypeNode typeNode, String typeArgumentName) {
|
ClassProperty prop, VmType type, String typeArgumentName) {
|
||||||
if (resolvePrimitive(typeNode)) {
|
if (resolvePrimitive(type)) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -644,7 +668,7 @@ public final class CommandSpecParser {
|
|||||||
"commandOptionUnsupportedType",
|
"commandOptionUnsupportedType",
|
||||||
prop.getName(),
|
prop.getName(),
|
||||||
typeArgumentName + " ",
|
typeArgumentName + " ",
|
||||||
typeNode.getSourceSection().getCharacters())
|
type.toString())
|
||||||
.withHint("Use a supported type or define a transformEach and/or transformAll function")
|
.withHint("Use a supported type or define a transformEach and/or transformAll function")
|
||||||
.build();
|
.build();
|
||||||
} else if (metavar == null) {
|
} else if (metavar == null) {
|
||||||
@@ -656,138 +680,252 @@ public final class CommandSpecParser {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean resolvePrimitive(TypeNode typeNode) {
|
private boolean resolvePrimitive(VmType type) {
|
||||||
if (typeNode instanceof TypeNode.NumberTypeNode) {
|
if (type instanceof VmType.ClassType ct) {
|
||||||
if (each == null)
|
if (ct.getVmClass() == BaseModule.getNumberClass()) {
|
||||||
each =
|
if (each == null) {
|
||||||
(rawValue, workingDirUri) -> {
|
each =
|
||||||
try {
|
(rawValue, workingDirUri) -> {
|
||||||
return Long.parseLong(rawValue);
|
try {
|
||||||
} catch (NumberFormatException e) {
|
return Long.parseLong(rawValue);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
try {
|
||||||
|
return Double.parseDouble(rawValue);
|
||||||
|
} catch (NumberFormatException e2) {
|
||||||
|
throw BadValue.invalid(rawValue, METAVAR_NUMBER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (all == null) {
|
||||||
|
all = this::allChooseLast;
|
||||||
|
}
|
||||||
|
if (multiple == null) {
|
||||||
|
multiple = false;
|
||||||
|
}
|
||||||
|
if (metavar == null) {
|
||||||
|
metavar = METAVAR_NUMBER;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else if (ct.getVmClass() == BaseModule.getFloatClass()) {
|
||||||
|
if (each == null) {
|
||||||
|
each =
|
||||||
|
(rawValue, workingDirUri) -> {
|
||||||
try {
|
try {
|
||||||
return Double.parseDouble(rawValue);
|
return Double.parseDouble(rawValue);
|
||||||
} catch (NumberFormatException e2) {
|
} catch (NumberFormatException e) {
|
||||||
throw BadValue.invalid(rawValue, METAVAR_NUMBER);
|
throw BadValue.invalid(rawValue, METAVAR_FLOAT);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
};
|
}
|
||||||
if (all == null) all = this::allChooseLast;
|
if (all == null) {
|
||||||
if (multiple == null) multiple = false;
|
all = this::allChooseLast;
|
||||||
if (metavar == null) metavar = METAVAR_NUMBER;
|
}
|
||||||
return true;
|
if (multiple == null) {
|
||||||
} else if (typeNode instanceof TypeNode.FloatTypeNode) {
|
multiple = false;
|
||||||
if (each == null)
|
}
|
||||||
each =
|
if (metavar == null) {
|
||||||
(rawValue, workingDirUri) -> {
|
metavar = METAVAR_FLOAT;
|
||||||
try {
|
}
|
||||||
return Double.parseDouble(rawValue);
|
return true;
|
||||||
} catch (NumberFormatException e) {
|
} else if (ct.getVmClass() == BaseModule.getIntClass()) {
|
||||||
throw BadValue.invalid(rawValue, METAVAR_FLOAT);
|
if (each == null) {
|
||||||
}
|
each = eachLong(Long.MIN_VALUE, Long.MAX_VALUE, METAVAR_INT);
|
||||||
};
|
}
|
||||||
if (all == null) all = this::allChooseLast;
|
if (all == null) {
|
||||||
if (multiple == null) multiple = false;
|
all = this::allChooseLast;
|
||||||
if (metavar == null) metavar = METAVAR_FLOAT;
|
}
|
||||||
return true;
|
if (multiple == null) {
|
||||||
} else if (typeNode instanceof TypeNode.IntTypeNode) {
|
multiple = false;
|
||||||
if (each == null) each = eachLong(Long.MIN_VALUE, Long.MAX_VALUE, METAVAR_INT);
|
}
|
||||||
if (all == null) all = this::allChooseLast;
|
if (metavar == null) {
|
||||||
if (multiple == null) multiple = false;
|
metavar = METAVAR_INT;
|
||||||
if (metavar == null) metavar = METAVAR_INT;
|
}
|
||||||
return true;
|
return true;
|
||||||
} else if (typeNode instanceof TypeNode.Int8TypeAliasTypeNode) {
|
} else if (ct.getVmClass() == BaseModule.getBooleanClass()) {
|
||||||
if (each == null) each = eachLong(Byte.MIN_VALUE, Byte.MAX_VALUE, METAVAR_INT8);
|
if (each == null) {
|
||||||
if (all == null) all = this::allChooseLast;
|
each =
|
||||||
if (multiple == null) multiple = false;
|
(rawValue, workingDirUri) -> {
|
||||||
if (metavar == null) metavar = METAVAR_INT8;
|
var value = rawValue.toLowerCase(Locale.ROOT);
|
||||||
return true;
|
if (TRUE_VALUES.contains(value)) {
|
||||||
} else if (typeNode instanceof TypeNode.Int16TypeAliasTypeNode) {
|
return true;
|
||||||
if (each == null) each = eachLong(Short.MIN_VALUE, Short.MAX_VALUE, METAVAR_INT16);
|
} else if (FALSE_VALUES.contains(value)) {
|
||||||
if (all == null) all = this::allChooseLast;
|
return false;
|
||||||
if (multiple == null) multiple = false;
|
}
|
||||||
if (metavar == null) metavar = METAVAR_INT16;
|
throw BadValue.invalid(rawValue, "boolean");
|
||||||
return true;
|
};
|
||||||
} else if (typeNode instanceof TypeNode.Int32TypeAliasTypeNode) {
|
}
|
||||||
if (each == null) each = eachLong(Integer.MIN_VALUE, Integer.MAX_VALUE, METAVAR_INT32);
|
if (all == null) {
|
||||||
if (all == null) all = this::allChooseLast;
|
all = this::allChooseLast;
|
||||||
if (multiple == null) multiple = false;
|
}
|
||||||
if (metavar == null) metavar = METAVAR_INT32;
|
if (multiple == null) {
|
||||||
return true;
|
multiple = false;
|
||||||
} else if (typeNode instanceof TypeNode.UIntTypeAliasTypeNode uIntTypeAliasTypeNode) {
|
}
|
||||||
var mask = uIntTypeAliasTypeNode.getMask();
|
if (metavar == null) {
|
||||||
if (all == null) all = this::allChooseLast;
|
metavar = METAVAR_BOOLEAN;
|
||||||
if (multiple == null) multiple = false;
|
}
|
||||||
if (mask == 0x000000000000FFFFL) {
|
return true;
|
||||||
if (each == null) each = eachLong(0, 0x000000000000FFFFL, METAVAR_UINT16);
|
} else if (ct.getVmClass() == BaseModule.getStringClass()) {
|
||||||
if (metavar == null) metavar = METAVAR_UINT16;
|
if (each == null) {
|
||||||
} else if (mask == 0x00000000FFFFFFFFL) {
|
each = (rawValue, workingDirUri) -> rawValue;
|
||||||
if (each == null) each = eachLong(0, 0x00000000FFFFFFFFL, METAVAR_UINT32);
|
}
|
||||||
if (metavar == null) metavar = METAVAR_UINT32;
|
if (all == null) {
|
||||||
} else {
|
all = this::allChooseLast;
|
||||||
if (each == null) each = eachLong(0, Long.MAX_VALUE, METAVAR_UINT);
|
}
|
||||||
if (metavar == null) metavar = METAVAR_UINT;
|
if (multiple == null) {
|
||||||
|
multiple = false;
|
||||||
|
}
|
||||||
|
if (metavar == null) {
|
||||||
|
metavar = METAVAR_STRING;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return true;
|
} else if (type instanceof VmType.AliasType at) {
|
||||||
} else if (typeNode instanceof TypeNode.UInt8TypeAliasTypeNode) {
|
if (at.getVmTypeAlias() == BaseModule.getInt8TypeAlias()) {
|
||||||
if (each == null) each = eachLong(0, 0x00000000000000FFL, METAVAR_UINT8);
|
if (each == null) {
|
||||||
if (all == null) all = this::allChooseLast;
|
each = eachLong(Byte.MIN_VALUE, Byte.MAX_VALUE, METAVAR_INT8);
|
||||||
if (multiple == null) multiple = false;
|
}
|
||||||
if (metavar == null) metavar = METAVAR_UINT8;
|
if (all == null) {
|
||||||
return true;
|
all = this::allChooseLast;
|
||||||
} else if (typeNode instanceof TypeNode.BooleanTypeNode) {
|
}
|
||||||
if (each == null)
|
if (multiple == null) {
|
||||||
|
multiple = false;
|
||||||
|
}
|
||||||
|
if (metavar == null) {
|
||||||
|
metavar = METAVAR_INT8;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else if (at.getVmTypeAlias() == BaseModule.getInt16TypeAlias()) {
|
||||||
|
if (each == null) {
|
||||||
|
each = eachLong(Short.MIN_VALUE, Short.MAX_VALUE, METAVAR_INT16);
|
||||||
|
}
|
||||||
|
if (all == null) {
|
||||||
|
all = this::allChooseLast;
|
||||||
|
}
|
||||||
|
if (multiple == null) {
|
||||||
|
multiple = false;
|
||||||
|
}
|
||||||
|
if (metavar == null) {
|
||||||
|
metavar = METAVAR_INT16;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else if (at.getVmTypeAlias() == BaseModule.getInt32TypeAlias()) {
|
||||||
|
if (each == null) {
|
||||||
|
each = eachLong(Integer.MIN_VALUE, Integer.MAX_VALUE, METAVAR_INT32);
|
||||||
|
}
|
||||||
|
if (all == null) {
|
||||||
|
all = this::allChooseLast;
|
||||||
|
}
|
||||||
|
if (multiple == null) {
|
||||||
|
multiple = false;
|
||||||
|
}
|
||||||
|
if (metavar == null) {
|
||||||
|
metavar = METAVAR_INT32;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else if (at.getVmTypeAlias() == BaseModule.getUInt8TypeAlias()) {
|
||||||
|
if (each == null) {
|
||||||
|
each = eachLong(0, 0x00000000000000FFL, METAVAR_UINT8);
|
||||||
|
}
|
||||||
|
if (all == null) {
|
||||||
|
all = this::allChooseLast;
|
||||||
|
}
|
||||||
|
if (multiple == null) {
|
||||||
|
multiple = false;
|
||||||
|
}
|
||||||
|
if (metavar == null) {
|
||||||
|
metavar = METAVAR_UINT8;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else if (at.getVmTypeAlias() == BaseModule.getUInt16TypeAlias()) {
|
||||||
|
if (each == null) {
|
||||||
|
each = eachLong(0, 0x000000000000FFFFL, METAVAR_UINT16);
|
||||||
|
}
|
||||||
|
if (all == null) {
|
||||||
|
all = this::allChooseLast;
|
||||||
|
}
|
||||||
|
if (multiple == null) {
|
||||||
|
multiple = false;
|
||||||
|
}
|
||||||
|
if (metavar == null) {
|
||||||
|
metavar = METAVAR_UINT16;
|
||||||
|
}
|
||||||
|
} else if (at.getVmTypeAlias() == BaseModule.getUInt32TypeAlias()) {
|
||||||
|
if (each == null) {
|
||||||
|
each = eachLong(0, 0x00000000FFFFFFFFL, METAVAR_UINT32);
|
||||||
|
}
|
||||||
|
if (all == null) {
|
||||||
|
all = this::allChooseLast;
|
||||||
|
}
|
||||||
|
if (multiple == null) {
|
||||||
|
multiple = false;
|
||||||
|
}
|
||||||
|
if (metavar == null) {
|
||||||
|
metavar = METAVAR_UINT32;
|
||||||
|
}
|
||||||
|
} else if (at.getVmTypeAlias() == BaseModule.getUIntTypeAlias()) {
|
||||||
|
if (each == null) {
|
||||||
|
each = eachLong(0, Long.MAX_VALUE, METAVAR_UINT);
|
||||||
|
}
|
||||||
|
if (all == null) {
|
||||||
|
all = this::allChooseLast;
|
||||||
|
}
|
||||||
|
if (multiple == null) {
|
||||||
|
multiple = false;
|
||||||
|
}
|
||||||
|
if (metavar == null) {
|
||||||
|
metavar = METAVAR_UINT;
|
||||||
|
}
|
||||||
|
} else if (at.getVmTypeAlias() == BaseModule.getCharTypeAlias()) {
|
||||||
|
if (each == null) {
|
||||||
|
each =
|
||||||
|
(rawValue, workingDirUri) -> {
|
||||||
|
if (rawValue.length() != 1) throw BadValue.invalid(rawValue, METAVAR_CHAR);
|
||||||
|
return rawValue;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (all == null) {
|
||||||
|
all = this::allChooseLast;
|
||||||
|
}
|
||||||
|
if (multiple == null) {
|
||||||
|
multiple = false;
|
||||||
|
}
|
||||||
|
if (metavar == null) {
|
||||||
|
metavar = METAVAR_CHAR;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (type instanceof VmType.UnionType ut && ut.isUnionOfStringLiterals()) {
|
||||||
|
var choices =
|
||||||
|
Arrays.stream(ut.getElementTypes())
|
||||||
|
.map(it -> ((VmType.StringLiteralType) it).getLiteral())
|
||||||
|
.toList();
|
||||||
|
var choiceSet = new HashSet<>(choices);
|
||||||
|
if (each == null) {
|
||||||
each =
|
each =
|
||||||
(rawValue, workingDirUri) -> {
|
(rawValue, workingDirUri) -> {
|
||||||
var value = rawValue.toLowerCase(Locale.ROOT);
|
if (!choiceSet.contains(rawValue)) {
|
||||||
if (TRUE_VALUES.contains(value)) {
|
|
||||||
return true;
|
|
||||||
} else if (FALSE_VALUES.contains(value)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
throw BadValue.invalid(rawValue, "boolean");
|
|
||||||
};
|
|
||||||
if (all == null) all = this::allChooseLast;
|
|
||||||
if (multiple == null) multiple = false;
|
|
||||||
if (metavar == null) metavar = METAVAR_BOOLEAN;
|
|
||||||
return true;
|
|
||||||
} else if (typeNode instanceof TypeNode.StringTypeNode) {
|
|
||||||
if (each == null) each = (rawValue, workingDirUri) -> rawValue;
|
|
||||||
if (all == null) all = this::allChooseLast;
|
|
||||||
if (multiple == null) multiple = false;
|
|
||||||
if (metavar == null) metavar = METAVAR_STRING;
|
|
||||||
return true;
|
|
||||||
} else if (typeNode instanceof TypeNode.TypeAliasTypeNode typeAliasTypeNode
|
|
||||||
&& typeAliasTypeNode.getVmTypeAlias() == BaseModule.getCharTypeAlias()) {
|
|
||||||
if (each == null)
|
|
||||||
each =
|
|
||||||
(rawValue, workingDirUri) -> {
|
|
||||||
if (rawValue.length() != 1) throw BadValue.invalid(rawValue, METAVAR_CHAR);
|
|
||||||
return rawValue;
|
|
||||||
};
|
|
||||||
if (all == null) all = this::allChooseLast;
|
|
||||||
if (multiple == null) multiple = false;
|
|
||||||
if (metavar == null) metavar = METAVAR_CHAR;
|
|
||||||
return true;
|
|
||||||
} else if (typeNode
|
|
||||||
instanceof TypeNode.UnionOfStringLiteralsTypeNode unionOfStringLiteralsTypeNode) {
|
|
||||||
var choices = unionOfStringLiteralsTypeNode.getStringLiterals().stream().sorted().toList();
|
|
||||||
if (each == null)
|
|
||||||
each =
|
|
||||||
(rawValue, workingDirUri) -> {
|
|
||||||
if (!unionOfStringLiteralsTypeNode.getStringLiterals().contains(rawValue)) {
|
|
||||||
throw BadValue.invalidChoice(rawValue, choices);
|
throw BadValue.invalidChoice(rawValue, choices);
|
||||||
}
|
}
|
||||||
return rawValue;
|
return rawValue;
|
||||||
};
|
};
|
||||||
if (all == null) all = this::allChooseLast;
|
}
|
||||||
if (multiple == null) multiple = false;
|
if (all == null) {
|
||||||
if (metavar == null) metavar = "[" + String.join(", ", choices) + "]";
|
all = this::allChooseLast;
|
||||||
if (completionCandidates == null)
|
}
|
||||||
completionCandidates = new Fixed(unionOfStringLiteralsTypeNode.getStringLiterals());
|
if (multiple == null) {
|
||||||
|
multiple = false;
|
||||||
|
}
|
||||||
|
if (metavar == null) {
|
||||||
|
metavar = "[" + String.join(", ", choices) + "]";
|
||||||
|
}
|
||||||
|
if (completionCandidates == null) {
|
||||||
|
completionCandidates = new Fixed(choiceSet);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
} else if (typeNode instanceof TypeNode.StringLiteralTypeNode stringLiteralTypeNode) {
|
} else if (type instanceof VmType.StringLiteralType slt) {
|
||||||
var choice = stringLiteralTypeNode.getLiteral();
|
var choice = slt.getLiteral();
|
||||||
if (each == null)
|
if (each == null) {
|
||||||
each =
|
each =
|
||||||
(rawValue, workingDirUri) -> {
|
(rawValue, workingDirUri) -> {
|
||||||
if (!rawValue.equals(choice)) {
|
if (!rawValue.equals(choice)) {
|
||||||
@@ -795,10 +933,19 @@ public final class CommandSpecParser {
|
|||||||
}
|
}
|
||||||
return rawValue;
|
return rawValue;
|
||||||
};
|
};
|
||||||
if (all == null) all = this::allChooseLast;
|
}
|
||||||
if (multiple == null) multiple = false;
|
if (all == null) {
|
||||||
if (metavar == null) metavar = "[" + choice + "]";
|
all = this::allChooseLast;
|
||||||
if (completionCandidates == null) completionCandidates = new Fixed(Set.of(choice));
|
}
|
||||||
|
if (multiple == null) {
|
||||||
|
multiple = false;
|
||||||
|
}
|
||||||
|
if (metavar == null) {
|
||||||
|
metavar = "[" + choice + "]";
|
||||||
|
}
|
||||||
|
if (completionCandidates == null) {
|
||||||
|
completionCandidates = new Fixed(Set.of(choice));
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -823,7 +970,10 @@ public final class CommandSpecParser {
|
|||||||
private static final Set<String> FALSE_VALUES = Set.of("false", "f", "0", "no", "n", "off");
|
private static final Set<String> FALSE_VALUES = Set.of("false", "f", "0", "no", "n", "off");
|
||||||
|
|
||||||
/** Sets each and metavar if they're not set */
|
/** Sets each and metavar if they're not set */
|
||||||
private void handleElement(TypeNode valueType, ClassProperty prop) {
|
private void handleElement(ClassProperty prop, VmType.ClassType type) {
|
||||||
|
if (!type.isParametric()) throw unsupportedOptionTypeArguments(prop, type);
|
||||||
|
var valueType = type.getTypeArguments()[0];
|
||||||
|
|
||||||
if (each != null && metavar != null) return;
|
if (each != null && metavar != null) return;
|
||||||
var transformValue =
|
var transformValue =
|
||||||
new OptionBehavior(each, all, multiple, metavar, completionCandidates)
|
new OptionBehavior(each, all, multiple, metavar, completionCandidates)
|
||||||
@@ -833,7 +983,11 @@ public final class CommandSpecParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Sets each and metavar if they're not set */
|
/** Sets each and metavar if they're not set */
|
||||||
private void handleEntry(TypeNode keyType, TypeNode valueType, ClassProperty prop) {
|
private void handleEntry(ClassProperty prop, VmType.ClassType type) {
|
||||||
|
if (!type.isParametric()) throw unsupportedOptionTypeArguments(prop, type);
|
||||||
|
var keyType = type.getTypeArguments()[0];
|
||||||
|
var valueType = type.getTypeArguments()[1];
|
||||||
|
|
||||||
if (each != null && metavar != null) return;
|
if (each != null && metavar != null) return;
|
||||||
var transformKey =
|
var transformKey =
|
||||||
new OptionBehavior(each, all, multiple, metavar, completionCandidates)
|
new OptionBehavior(each, all, multiple, metavar, completionCandidates)
|
||||||
@@ -841,7 +995,7 @@ public final class CommandSpecParser {
|
|||||||
var transformValue =
|
var transformValue =
|
||||||
new OptionBehavior(each, all, multiple, metavar, completionCandidates)
|
new OptionBehavior(each, all, multiple, metavar, completionCandidates)
|
||||||
.resolveTypeArgument(prop, resolveType(valueType).getFirst(), "value");
|
.resolveTypeArgument(prop, resolveType(valueType).getFirst(), "value");
|
||||||
if (each == null)
|
if (each == null) {
|
||||||
each =
|
each =
|
||||||
(rawValue, workingDirUri) -> {
|
(rawValue, workingDirUri) -> {
|
||||||
var split = rawValue.split("=", 2);
|
var split = rawValue.split("=", 2);
|
||||||
@@ -852,7 +1006,10 @@ public final class CommandSpecParser {
|
|||||||
transformKey.getEach().apply(split[0], workingDirUri),
|
transformKey.getEach().apply(split[0], workingDirUri),
|
||||||
transformValue.getEach().apply(split[1], workingDirUri));
|
transformValue.getEach().apply(split[1], workingDirUri));
|
||||||
};
|
};
|
||||||
if (metavar == null) metavar = transformKey.getMetavar() + "=" + transformValue.getMetavar();
|
}
|
||||||
|
if (metavar == null) {
|
||||||
|
metavar = transformKey.getMetavar() + "=" + transformValue.getMetavar();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private @Nullable Object allChooseLast(List<Object> values, URI workingDirUri) {
|
private @Nullable Object allChooseLast(List<Object> values, URI workingDirUri) {
|
||||||
|
|||||||
@@ -223,7 +223,7 @@ public final class MirrorFactories {
|
|||||||
.addTypedProperty(
|
.addTypedProperty(
|
||||||
"referent",
|
"referent",
|
||||||
typeNode -> {
|
typeNode -> {
|
||||||
var clazz = typeNode.getVmClass();
|
var clazz = typeNode.getType().getVmClass();
|
||||||
assert clazz != null;
|
assert clazz != null;
|
||||||
return clazz.getMirror();
|
return clazz.getMirror();
|
||||||
})
|
})
|
||||||
@@ -232,11 +232,7 @@ public final class MirrorFactories {
|
|||||||
typeAliasTypeFactory
|
typeAliasTypeFactory
|
||||||
.addTypedProperty(
|
.addTypedProperty(
|
||||||
"referent",
|
"referent",
|
||||||
typeNode -> {
|
typeNode -> ((VmType.AliasType) typeNode.getType()).getVmTypeAlias().getMirror())
|
||||||
var alias = typeNode.getVmTypeAlias();
|
|
||||||
assert alias != null;
|
|
||||||
return alias.getMirror();
|
|
||||||
})
|
|
||||||
.addListProperty("typeArguments", TypeNode::getTypeArgumentMirrors);
|
.addListProperty("typeArguments", TypeNode::getTypeArgumentMirrors);
|
||||||
|
|
||||||
declaredTypeFactory
|
declaredTypeFactory
|
||||||
@@ -251,7 +247,8 @@ public final class MirrorFactories {
|
|||||||
.addListProperty("parameterTypes", Pair::getFirst)
|
.addListProperty("parameterTypes", Pair::getFirst)
|
||||||
.addTypedProperty("returnType", Pair::getSecond);
|
.addTypedProperty("returnType", Pair::getSecond);
|
||||||
|
|
||||||
stringLiteralTypeFactory.addStringProperty("value", StringLiteralTypeNode::getLiteral);
|
stringLiteralTypeFactory.addStringProperty(
|
||||||
|
"value", typeNode -> ((VmType.StringLiteralType) typeNode.getType()).getLiteral());
|
||||||
|
|
||||||
stringLiteralTypeFactory2.addStringProperty("value", Property.identity());
|
stringLiteralTypeFactory2.addStringProperty("value", Property.identity());
|
||||||
|
|
||||||
|
|||||||
@@ -244,10 +244,15 @@ public final class VmClass extends VmValue {
|
|||||||
checkAbstractMethods();
|
checkAbstractMethods();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TruffleBoundary
|
||||||
public int getTypeParameterCount() {
|
public int getTypeParameterCount() {
|
||||||
return typeParameters.size();
|
return typeParameters.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<TypeParameter> getTypeParameters() {
|
||||||
|
return typeParameters;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the property with the given name declared in this class, or {@code null} if no such
|
* Returns the property with the given name declared in this class, or {@code null} if no such
|
||||||
* property was found. Does return local properties.
|
* property was found. Does return local properties.
|
||||||
@@ -298,6 +303,13 @@ public final class VmClass extends VmValue {
|
|||||||
return (VmTyped) module;
|
return (VmTyped) module;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public VmClass getModuleClass() {
|
||||||
|
if (classInfo.isModuleClass()) return this;
|
||||||
|
var module = prototype.getEnclosingOwner();
|
||||||
|
assert module != null;
|
||||||
|
return module.getVmClass();
|
||||||
|
}
|
||||||
|
|
||||||
public VmTyped getModuleMirror() {
|
public VmTyped getModuleMirror() {
|
||||||
return getModule().getModuleInfo().getMirror(getModule());
|
return getModule().getModuleInfo().getMirror(getModule());
|
||||||
}
|
}
|
||||||
@@ -393,6 +405,11 @@ public final class VmClass extends VmValue {
|
|||||||
return superclass;
|
return superclass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public @Nullable VmType getSupertype() {
|
||||||
|
if (supertypeNode == null) return null;
|
||||||
|
return supertypeNode.getType();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VmTyped getPrototype() {
|
public VmTyped getPrototype() {
|
||||||
return prototype;
|
return prototype;
|
||||||
@@ -752,7 +769,7 @@ public final class VmClass extends VmValue {
|
|||||||
return getDisplayName();
|
return getDisplayName();
|
||||||
}
|
}
|
||||||
|
|
||||||
private UnmodifiableEconomicMap<Identifier, ClassProperty> getAllProperties() {
|
public UnmodifiableEconomicMap<Identifier, ClassProperty> getAllProperties() {
|
||||||
synchronized (allPropertiesLock) {
|
synchronized (allPropertiesLock) {
|
||||||
if (__allProperties == null) {
|
if (__allProperties == null) {
|
||||||
// can't do this in ClassNode because it requires a fully initialized inheritance hierarchy
|
// can't do this in ClassNode because it requires a fully initialized inheritance hierarchy
|
||||||
|
|||||||
@@ -133,6 +133,6 @@ public abstract class VmListingOrMapping extends VmObject {
|
|||||||
if (typeCastNode == null) {
|
if (typeCastNode == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return typeCastNode.getTypeNode().isEquivalentTo(typeNode);
|
return typeCastNode.getTypeNode().getType().equals(typeNode.getType());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -18,7 +18,7 @@ package org.pkl.core.runtime;
|
|||||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||||
import com.oracle.truffle.api.nodes.DirectCallNode;
|
import com.oracle.truffle.api.nodes.DirectCallNode;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -27,12 +27,11 @@ import java.util.function.BiConsumer;
|
|||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.pkl.core.Composite;
|
import org.pkl.core.Composite;
|
||||||
import org.pkl.core.PClass;
|
|
||||||
import org.pkl.core.PClassInfo;
|
|
||||||
import org.pkl.core.PType;
|
|
||||||
import org.pkl.core.PklBugException;
|
|
||||||
import org.pkl.core.Reference;
|
import org.pkl.core.Reference;
|
||||||
import org.pkl.core.TypeAlias;
|
import org.pkl.core.runtime.VmType.NonFinalModuleType;
|
||||||
|
import org.pkl.core.runtime.VmType.NonFinalThisType;
|
||||||
|
import org.pkl.core.runtime.VmType.NothingType;
|
||||||
|
import org.pkl.core.runtime.VmType.UnknownType;
|
||||||
import org.pkl.core.util.paguro.RrbTree;
|
import org.pkl.core.util.paguro.RrbTree;
|
||||||
import org.pkl.core.util.paguro.RrbTree.ImRrbt;
|
import org.pkl.core.util.paguro.RrbTree.ImRrbt;
|
||||||
|
|
||||||
@@ -41,10 +40,12 @@ public final class VmReference extends VmValue {
|
|||||||
private final VmTyped domain;
|
private final VmTyped domain;
|
||||||
private final Object data;
|
private final Object data;
|
||||||
private final ImRrbt<VmTyped> path;
|
private final ImRrbt<VmTyped> path;
|
||||||
// candidate types can only be: PType.Class, PType.Alias (only preservedAliasTypes),
|
// candidate types can only be: VmType.ClassType, VmType.AliasType (only preservedAliasTypes),
|
||||||
// PType.StringLiteral, PType.UNKNOWN, PType.Function, PType.TypeVariable, or PType.Union
|
// VmType.StringLiteralType, VmType.Unknown.INSTANCE, VmType.FunctionType,
|
||||||
|
// VmType.TypeVariableTybe, or
|
||||||
|
// VmType.UnionType
|
||||||
// (containing only the previous; flattened)
|
// (containing only the previous; flattened)
|
||||||
private final PType referentType;
|
private final VmType referentType;
|
||||||
|
|
||||||
private boolean forced = false;
|
private boolean forced = false;
|
||||||
|
|
||||||
@@ -55,12 +56,15 @@ public final class VmReference extends VmValue {
|
|||||||
.toTyped(RefModule.getAccessClass());
|
.toTyped(RefModule.getAccessClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
@TruffleBoundary
|
|
||||||
public VmReference(VmTyped domain, VmClass clazz, Object data) {
|
public VmReference(VmTyped domain, VmClass clazz, Object data) {
|
||||||
this(domain, data, RrbTree.empty(), normalizeTypes(new PType.Class(clazz.export())));
|
this(
|
||||||
|
domain,
|
||||||
|
data,
|
||||||
|
RrbTree.empty(),
|
||||||
|
normalizeTypes(new VmType.ClassType(clazz), clazz, clazz.getModuleClass()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public VmReference(VmTyped domain, Object data, ImRrbt<VmTyped> path, PType referentType) {
|
public VmReference(VmTyped domain, Object data, ImRrbt<VmTyped> path, VmType referentType) {
|
||||||
this.domain = domain;
|
this.domain = domain;
|
||||||
this.data = data;
|
this.data = data;
|
||||||
this.referentType = referentType;
|
this.referentType = referentType;
|
||||||
@@ -79,7 +83,7 @@ public final class VmReference extends VmValue {
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PType getReferentType() {
|
public VmType getReferentType() {
|
||||||
return referentType;
|
return referentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,71 +92,67 @@ public final class VmReference extends VmValue {
|
|||||||
// * transforming T? into T|Null
|
// * transforming T? into T|Null
|
||||||
// * dereferencing aliases (except for well-known stdlib alias types)
|
// * dereferencing aliases (except for well-known stdlib alias types)
|
||||||
// * flattening unions
|
// * flattening unions
|
||||||
// * drop PType.Function and PType.TypeVariable
|
// * replace VmType.ModuleType with appropriate VmType.ClassType
|
||||||
private static PType normalizeTypes(
|
// * drop VmType.FunctionType and VmType.TypeVariableType
|
||||||
PType type, @Nullable PType thisClass, @Nullable PType moduleClass) {
|
@TruffleBoundary
|
||||||
var types = new HashSet<PType>();
|
private static VmType normalizeTypes(VmType type, VmClass thisClass, VmClass moduleClass) {
|
||||||
normalizeTypes(type, types, thisClass, moduleClass);
|
var types = new HashSet<VmType>();
|
||||||
|
normalizeTypes(type, thisClass, moduleClass, types);
|
||||||
return minimizeTypes(types);
|
return minimizeTypes(types);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PType normalizeTypes(PType type) {
|
private static VmType minimizeTypes(Set<VmType> types) {
|
||||||
return normalizeTypes(type, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static PType minimizeTypes(Set<PType> types) {
|
|
||||||
if (types.size() == 1) return types.iterator().next();
|
if (types.size() == 1) return types.iterator().next();
|
||||||
// optimization: unknown allows all references, erase all candidates to only unknown
|
// optimization: unknown allows all references, erase all candidates to only unknown
|
||||||
if (types.contains(PType.UNKNOWN)) return PType.UNKNOWN;
|
if (types.contains(UnknownType.INSTANCE)) return UnknownType.INSTANCE;
|
||||||
// optimization: All allows all references, erase all candidates to only All
|
// optimization: All allows all references, erase all candidates to only All
|
||||||
if (containsClass(types, BaseModule.getAnyClass().export()))
|
if (containsClass(types, BaseModule.getAnyClass())) {
|
||||||
return new PType.Class(BaseModule.getAnyClass().export());
|
return new VmType.ClassType(BaseModule.getAnyClass());
|
||||||
var typesList = new ArrayList<>(types);
|
}
|
||||||
typesList.sort(Comparator.comparing(Object::toString));
|
var typeArray = types.toArray(new VmType[0]);
|
||||||
return new PType.Union(typesList);
|
Arrays.sort(typeArray, Comparator.comparing(Object::toString));
|
||||||
|
return new VmType.UnionType(-1, typeArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void normalizeTypes(
|
private static void normalizeTypes(
|
||||||
PType type, Set<PType> result, @Nullable PType thisClass, @Nullable PType moduleClass) {
|
VmType type, VmClass thisClass, VmClass moduleClass, Set<VmType> result) {
|
||||||
if (type == PType.UNKNOWN || type == PType.NOTHING || type instanceof PType.StringLiteral) {
|
if (type == UnknownType.INSTANCE
|
||||||
|
|| type == NothingType.INSTANCE
|
||||||
|
|| type instanceof VmType.StringLiteralType) {
|
||||||
result.add(type);
|
result.add(type);
|
||||||
} else if (type instanceof PType.Class clazz) {
|
} else if (type instanceof VmType.ClassType ct) {
|
||||||
if (clazz.getTypeArguments().isEmpty()) {
|
if (!ct.isParametric()) {
|
||||||
// if a generic type is used without type arguments, it needs to be normalized so all args
|
result.add(ct);
|
||||||
// are unknown; i.e. with bare List/Map/etc. type annotations (via FinalClassTypeNode).
|
|
||||||
var typeParameterCount = clazz.getPClass().getTypeParameters().size();
|
|
||||||
result.add(
|
|
||||||
typeParameterCount == 0
|
|
||||||
? clazz
|
|
||||||
: new PType.Class(
|
|
||||||
clazz.getPClass(), Collections.nCopies(typeParameterCount, PType.UNKNOWN)));
|
|
||||||
} else {
|
} else {
|
||||||
var typeArgs = new ArrayList<PType>(clazz.getTypeArguments().size());
|
result.add(
|
||||||
for (var arg : clazz.getTypeArguments()) {
|
ct.withTypeArguments(
|
||||||
typeArgs.add(normalizeTypes(arg, thisClass, moduleClass));
|
Arrays.stream(ct.getTypeArguments())
|
||||||
}
|
.map(arg -> normalizeTypes(arg, thisClass, moduleClass))
|
||||||
result.add(new PType.Class(clazz.getPClass(), typeArgs));
|
.toArray(VmType[]::new)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// normalize `T?` to `T | Null`
|
// normalize `T?` to `T | Null`
|
||||||
else if (type instanceof PType.Nullable nullable) {
|
else if (type instanceof VmType.NullableType nullable) {
|
||||||
normalizeTypes(nullable.getBaseType(), result, thisClass, moduleClass);
|
normalizeTypes(nullable.getElementType(), thisClass, moduleClass, result);
|
||||||
result.add(new PType.Class(BaseModule.getNullClass().export()));
|
result.add(new VmType.ClassType(BaseModule.getNullClass()));
|
||||||
// erase `T(someConstraint)` to `T`
|
// erase `T(someConstraint)` to `T`
|
||||||
} else if (type instanceof PType.Constrained constrained) {
|
} else if (type instanceof VmType.ConstrainedType constrained) {
|
||||||
normalizeTypes(constrained.getBaseType(), result, thisClass, moduleClass);
|
normalizeTypes(constrained.getBaseType(), thisClass, moduleClass, result);
|
||||||
} else if (type instanceof PType.Alias alias) {
|
} else if (type instanceof VmType.AliasType alias) {
|
||||||
if (isPreservedTypeAlias(alias.getTypeAlias())) {
|
if (isPreservedTypeAlias(alias.getVmTypeAlias())) {
|
||||||
result.add(alias);
|
result.add(alias);
|
||||||
} else {
|
} else {
|
||||||
normalizeTypes(alias.getAliasedType(), result, thisClass, moduleClass);
|
normalizeTypes(
|
||||||
|
alias.getAliasedType(),
|
||||||
|
alias.getVmTypeAlias().getModuleClass(),
|
||||||
|
alias.getVmTypeAlias().getModuleClass(),
|
||||||
|
result);
|
||||||
}
|
}
|
||||||
} else if (type instanceof PType.Union union) {
|
} else if (type instanceof VmType.UnionType union) {
|
||||||
for (var t : union.getElementTypes()) {
|
for (var t : union.getElementTypes()) {
|
||||||
normalizeTypes(t, result, thisClass, moduleClass);
|
normalizeTypes(t, thisClass, moduleClass, result);
|
||||||
}
|
}
|
||||||
} else if (type == PType.THIS) {
|
} else if (type instanceof NonFinalThisType) {
|
||||||
assert thisClass != null;
|
|
||||||
// there are 4 entrypoints here:
|
// there are 4 entrypoints here:
|
||||||
// 1. init via the Reference constructor can only normalize an unparameterized PType.Class
|
// 1. init via the Reference constructor can only normalize an unparameterized PType.Class
|
||||||
// 2. typecheck via ReferenceTypeNode erases self types to their actual PType.Class
|
// 2. typecheck via ReferenceTypeNode erases self types to their actual PType.Class
|
||||||
@@ -161,13 +161,11 @@ public final class VmReference extends VmValue {
|
|||||||
// 4. property access uses the enclosing receiver's class to substitute for these self types
|
// 4. property access uses the enclosing receiver's class to substitute for these self types
|
||||||
// only property access and typecheck can produce THIS or MODULE.
|
// only property access and typecheck can produce THIS or MODULE.
|
||||||
// getCandidatePropertyType and referentTypeIsSubtypeOf always pass non-null `thisClass`.
|
// getCandidatePropertyType and referentTypeIsSubtypeOf always pass non-null `thisClass`.
|
||||||
result.add(thisClass);
|
result.add(new VmType.ClassType(thisClass));
|
||||||
} else if (type == PType.MODULE) {
|
} else if (type instanceof NonFinalModuleType) {
|
||||||
// this can be incorrect for usage of the module type in a class's property type annotation,
|
// this can be incorrect for usage of the module type in a class's property type annotation,
|
||||||
// which is deprecated!!
|
// which is deprecated!!
|
||||||
assert moduleClass != null;
|
result.add(new VmType.ClassType(moduleClass));
|
||||||
// see PType.THIS case above
|
|
||||||
result.add(moduleClass);
|
|
||||||
} else {
|
} else {
|
||||||
// remaining types: PType.Function, PType.TypeVariable. no normalizing needed; TypeVariable
|
// remaining types: PType.Function, PType.TypeVariable. no normalizing needed; TypeVariable
|
||||||
// gets replaced upon instantiation, and Function can bubble up to users as a reference error
|
// gets replaced upon instantiation, and Function can bubble up to users as a reference error
|
||||||
@@ -177,9 +175,9 @@ public final class VmReference extends VmValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Iterable<PType> iterateTypes(PType t) {
|
private static VmType[] iterateTypes(VmType t) {
|
||||||
if (t instanceof PType.Union union) return union.getElementTypes();
|
if (t instanceof VmType.UnionType union) return union.getElementTypes();
|
||||||
return Collections.singleton(t);
|
return new VmType[] {t};
|
||||||
}
|
}
|
||||||
|
|
||||||
public VmReference withPropertyAccess(Identifier property) {
|
public VmReference withPropertyAccess(Identifier property) {
|
||||||
@@ -197,8 +195,8 @@ public final class VmReference extends VmValue {
|
|||||||
|
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
private VmReference withAccess(
|
private VmReference withAccess(
|
||||||
BiConsumer<PType, Set<PType>> checkCandidate, Supplier<VmTyped> makeAccess) {
|
BiConsumer<VmType, Set<VmType>> checkCandidate, Supplier<VmTyped> makeAccess) {
|
||||||
Set<PType> candidates = new HashSet<>();
|
Set<VmType> candidates = new HashSet<>();
|
||||||
for (var t : iterateTypes(referentType)) {
|
for (var t : iterateTypes(referentType)) {
|
||||||
checkCandidate.accept(t, candidates);
|
checkCandidate.accept(t, candidates);
|
||||||
}
|
}
|
||||||
@@ -206,51 +204,51 @@ public final class VmReference extends VmValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("DuplicatedCode")
|
@SuppressWarnings("DuplicatedCode")
|
||||||
private static void getCandidatePropertyType(PType type, String property, Set<PType> result) {
|
private static void getCandidatePropertyType(VmType type, String property, Set<VmType> result) {
|
||||||
if (type == PType.UNKNOWN) {
|
if (type == UnknownType.INSTANCE) {
|
||||||
result.add(type);
|
result.add(type);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// restriction: only class types can have their properties referenced
|
// restriction: only class types can have their properties referenced
|
||||||
if (!(type instanceof PType.Class clazz)) {
|
if (!(type instanceof VmType.AbstractClassType ct)) {
|
||||||
throw new VmReferenceAccessError(type, VmReferenceAccessErrorType.CANNOT_FIND_MEMBER);
|
throw new VmReferenceAccessError(type, VmReferenceAccessErrorType.CANNOT_FIND_MEMBER);
|
||||||
}
|
}
|
||||||
if (clazz.getPClass().getInfo() == PClassInfo.Dynamic) {
|
if (ct.getVmClass().isDynamicClass()) {
|
||||||
if (property.equals("default")) {
|
if (property.equals("default")) {
|
||||||
// restriction: cannot reference Dynamic.default
|
// restriction: cannot reference Dynamic.default
|
||||||
throw new VmReferenceAccessError(type, VmReferenceAccessErrorType.DEFAULT_MEMBER);
|
throw new VmReferenceAccessError(type, VmReferenceAccessErrorType.DEFAULT_MEMBER);
|
||||||
}
|
}
|
||||||
result.add(PType.UNKNOWN);
|
result.add(UnknownType.INSTANCE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// restriction: cannot reference Listing/Mapping.default
|
// restriction: cannot reference Listing/Mapping.default
|
||||||
if (clazz.getPClass().getInfo() == PClassInfo.Listing
|
if (ct.getVmClass().isListingClass() || ct.getVmClass().isMappingClass()) {
|
||||||
|| clazz.getPClass().getInfo() == PClassInfo.Mapping) {
|
|
||||||
var errorType =
|
var errorType =
|
||||||
property.equals("default")
|
property.equals("default")
|
||||||
? VmReferenceAccessErrorType.DEFAULT_MEMBER
|
? VmReferenceAccessErrorType.DEFAULT_MEMBER
|
||||||
: VmReferenceAccessErrorType.CANNOT_FIND_MEMBER;
|
: VmReferenceAccessErrorType.CANNOT_FIND_MEMBER;
|
||||||
throw new VmReferenceAccessError(type, errorType);
|
throw new VmReferenceAccessError(type, errorType);
|
||||||
}
|
}
|
||||||
var baseModule = BaseModule.getModuleClass().export();
|
var baseModule = BaseModule.getModuleClass();
|
||||||
// restriction: cannot reference Module.output.
|
// restriction: cannot reference Module.output.
|
||||||
// generalized: properties originally defined in external classes; the only extant example.
|
// generalized: properties originally defined in external classes; the only extant example.
|
||||||
// This is implemented specifically because this is the only case where an external class
|
// This is implemented specifically because this is the only case where an external class
|
||||||
// containing a property can be subclassed.
|
// containing a property can be subclassed.
|
||||||
// And this can't check prop.getOwner().isExternal() because fully overriding the property with
|
// And this can't check prop.getOwner().isExternal() because fully overriding the property with
|
||||||
// a new type annotation means the owner isn't Module.
|
// a new type annotation means the owner isn't Module.
|
||||||
if (clazz.getPClass().isSubclassOf(baseModule) && property.equals("output")) {
|
if (ct.getVmClass().isSubclassOf(baseModule) && property.equals("output")) {
|
||||||
throw new VmReferenceAccessError(
|
throw new VmReferenceAccessError(
|
||||||
new PType.Class(baseModule), VmReferenceAccessErrorType.EXTERNAL_CLASS);
|
new VmType.ClassType(baseModule), VmReferenceAccessErrorType.EXTERNAL_CLASS);
|
||||||
}
|
}
|
||||||
|
|
||||||
// dot access on `Reference<D, Null>` gives `Reference<D, Null>`
|
// dot access on `Reference<D, Null>` gives `Reference<D, Null>`
|
||||||
if (clazz.getPClass().getInfo() == PClassInfo.Null) {
|
if (ct.getVmClass().isNullClass()) {
|
||||||
result.add(clazz);
|
result.add(ct);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var prop = clazz.getPClass().getAllProperties().get(property);
|
var prop = ct.getVmClass().getAllProperties().get(Identifier.get(property));
|
||||||
|
//noinspection ConstantValue
|
||||||
if (prop == null) {
|
if (prop == null) {
|
||||||
throw new VmReferenceAccessError(type, VmReferenceAccessErrorType.CANNOT_FIND_MEMBER);
|
throw new VmReferenceAccessError(type, VmReferenceAccessErrorType.CANNOT_FIND_MEMBER);
|
||||||
}
|
}
|
||||||
@@ -260,58 +258,57 @@ public final class VmReference extends VmValue {
|
|||||||
throw new VmReferenceAccessError(type, VmReferenceAccessErrorType.EXTERNAL_MEMBER);
|
throw new VmReferenceAccessError(type, VmReferenceAccessErrorType.EXTERNAL_MEMBER);
|
||||||
}
|
}
|
||||||
|
|
||||||
normalizeTypes(
|
// this handles the object-prop-in-class case because VmClass.getAllProperties() omits
|
||||||
prop.getType(), result, clazz, new PType.Class(clazz.getPClass().getModuleClass()));
|
// properties without type annotations that are defined in a superclass. e.g.:
|
||||||
}
|
// ```
|
||||||
|
// open class A { prop: String }
|
||||||
private static PClassInfo<?> getClassInfo(Object value) {
|
// class B extends A { prop = "foo" }
|
||||||
if (value instanceof VmValue vmValue) return vmValue.getVmClass().getPClassInfo();
|
// ```
|
||||||
if (value instanceof String) return PClassInfo.String;
|
// In this case `VmClass[B].getAllProperties().get(<prop>)` will be A's prop, not B's
|
||||||
if (value instanceof Boolean) return PClassInfo.Boolean;
|
var propTypeNode = prop.getTypeNode();
|
||||||
if (value instanceof Long) return PClassInfo.Int;
|
var propType =
|
||||||
if (value instanceof Double) return PClassInfo.Float;
|
propTypeNode == null ? VmType.UnknownType.INSTANCE : propTypeNode.getTypeNode().getType();
|
||||||
throw new PklBugException("Not a Pkl value: " + value.getClass());
|
normalizeTypes(propType, ct.getVmClass(), ct.getVmClass().getModuleClass(), result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("DuplicatedCode")
|
@SuppressWarnings("DuplicatedCode")
|
||||||
private static void getCandidateSubscriptType(PType type, Object key, Set<PType> result) {
|
private static void getCandidateSubscriptType(VmType type, Object key, Set<VmType> result) {
|
||||||
if (type == PType.UNKNOWN) {
|
if (type == UnknownType.INSTANCE) {
|
||||||
result.add(type);
|
result.add(type);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!(type instanceof PType.Class clazz)) {
|
if (!(type instanceof VmType.ClassType ct)) {
|
||||||
throw new VmReferenceAccessError(type, VmReferenceAccessErrorType.CANNOT_FIND_MEMBER);
|
throw new VmReferenceAccessError(type, VmReferenceAccessErrorType.CANNOT_FIND_MEMBER);
|
||||||
}
|
}
|
||||||
if (clazz.getPClass().getInfo() == PClassInfo.Dynamic) {
|
var clazz = ct.getVmClass();
|
||||||
result.add(PType.UNKNOWN);
|
if (clazz.isDynamicClass()) {
|
||||||
|
result.add(UnknownType.INSTANCE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (clazz.getPClass().getInfo() == PClassInfo.Listing
|
if (clazz.isListingClass() || clazz.isListClass()) {
|
||||||
|| clazz.getPClass().getInfo() == PClassInfo.List) {
|
|
||||||
if (!(key instanceof Long)) {
|
if (!(key instanceof Long)) {
|
||||||
throw new VmReferenceAccessError(type, VmReferenceAccessErrorType.CANNOT_FIND_MEMBER);
|
throw new VmReferenceAccessError(type, VmReferenceAccessErrorType.CANNOT_FIND_MEMBER);
|
||||||
}
|
}
|
||||||
normalizeTypes(clazz.getTypeArguments().get(0), result, null, null);
|
normalizeTypes(ct.getTypeArguments()[0], clazz, clazz.getModuleClass(), result);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (clazz.getPClass().getInfo() == PClassInfo.Mapping
|
if (clazz.isMappingClass() || clazz.isMapClass()) {
|
||||||
|| clazz.getPClass().getInfo() == PClassInfo.Map) {
|
var typeArgs = ct.getTypeArguments();
|
||||||
var typeArgs = clazz.getTypeArguments();
|
var keyTypes = normalizeTypes(typeArgs[0], clazz, clazz.getModuleClass());
|
||||||
var keyTypes = normalizeTypes(typeArgs.get(0));
|
|
||||||
for (var kt : iterateTypes(keyTypes)) {
|
for (var kt : iterateTypes(keyTypes)) {
|
||||||
if (kt == PType.UNKNOWN
|
if (kt == UnknownType.INSTANCE
|
||||||
|| (kt instanceof PType.Class klazz && klazz.getPClass().getInfo() == getClassInfo(key))
|
|| (kt instanceof VmType.ClassType klazz && klazz.getVmClass() == VmUtils.getClass(key))
|
||||||
|| (kt instanceof PType.StringLiteral stringLiteral
|
|| (kt instanceof VmType.StringLiteralType stringLiteral
|
||||||
&& stringLiteral.getLiteral().equals(key))) {
|
&& stringLiteral.getLiteral().equals(key))) {
|
||||||
normalizeTypes(typeArgs.get(1), result, null, null);
|
normalizeTypes(typeArgs[1], clazz, clazz.getModuleClass(), result);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// subscript access on `Reference<D, Null>` gives `Reference<D, Null>`
|
// subscript access on `Reference<D, Null>` gives `Reference<D, Null>`
|
||||||
if (clazz.getPClass().getInfo() == PClassInfo.Null) {
|
if (clazz == BaseModule.getNullClass()) {
|
||||||
result.add(clazz);
|
result.add(ct);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,151 +318,52 @@ public final class VmReference extends VmValue {
|
|||||||
/**
|
/**
|
||||||
* Tells if this reference's referent type is a subtype of {@code type}. Does not check domain.
|
* Tells if this reference's referent type is a subtype of {@code type}. Does not check domain.
|
||||||
*/
|
*/
|
||||||
@TruffleBoundary
|
public boolean referentTypeIsSubtypeOf(VmType type, VmClass thisClass, VmClass moduleClass) {
|
||||||
public boolean referentTypeIsSubtypeOf(PType type, PClass thisClass, PClass moduleClass) {
|
|
||||||
// fast path: if referent is unknown it can match any type check
|
// fast path: if referent is unknown it can match any type check
|
||||||
if (referentType == PType.UNKNOWN) {
|
if (referentType == UnknownType.INSTANCE) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var checkType = normalizeTypes(type, new PType.Class(thisClass), new PType.Class(moduleClass));
|
var checkType = normalizeTypes(type, thisClass, moduleClass);
|
||||||
// fast path: short circuit if any referent is accepted
|
// fast path: short circuit if any referent is accepted
|
||||||
if (checkType == PType.UNKNOWN || isClass(checkType, BaseModule.getAnyClass().export())) {
|
if (checkType == UnknownType.INSTANCE || isClass(checkType, BaseModule.getAnyClass())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// fast path: short circuit if nothing is accepted
|
// fast path: short circuit if nothing is accepted
|
||||||
if (checkType == PType.NOTHING) {
|
if (checkType == NothingType.INSTANCE) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return isSubtype(referentType, checkType);
|
return doReferentTypeIsSubtypeOf(checkType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean containsClass(Set<PType> types, PClass pClass) {
|
@TruffleBoundary
|
||||||
|
private boolean doReferentTypeIsSubtypeOf(VmType checkType) {
|
||||||
|
return referentType.isSubtypeOf(checkType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean containsClass(Set<VmType> types, VmClass clazz) {
|
||||||
for (var t : types) {
|
for (var t : types) {
|
||||||
if (isClass(t, pClass)) return true;
|
if (isClass(t, clazz)) return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isClass(PType t, PClass pClass) {
|
private static boolean isClass(VmType t, VmClass clazz) {
|
||||||
return t instanceof PType.Class clazz && clazz.getPClass() == pClass;
|
return t instanceof VmType.ClassType ct && ct.getVmClass() == clazz;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isSubtype(PType a, PType b) {
|
private static boolean isIntTypeAlias(VmTypeAlias t) {
|
||||||
// checks if A is a subtype of B
|
return t == BaseModule.getInt8TypeAlias()
|
||||||
// cases (A -> B)
|
|| t == BaseModule.getInt16TypeAlias()
|
||||||
// * A == B
|
|| t == BaseModule.getInt32TypeAlias()
|
||||||
// * StringLiteral -> StringLiteral: if literals are the same
|
|| t == BaseModule.getUInt8TypeAlias()
|
||||||
// * StringLiteral -> Class: B is String
|
|| t == BaseModule.getUInt16TypeAlias()
|
||||||
// * Char Alias -> Char Alias, StringLiteral (known single character)
|
|| t == BaseModule.getUInt32TypeAlias()
|
||||||
// * Int Alias -> Class: B is a subtype of Number (Int|Float|Number)
|
|| t == BaseModule.getUIntTypeAlias();
|
||||||
// * Int Alias -> Alias
|
|
||||||
// * same alias
|
|
||||||
// * Int8 is Int16|Int32
|
|
||||||
// * Int16 is Int32
|
|
||||||
// * UInt8 is Int16|Int32|Uint16|UInt32|UInt
|
|
||||||
// * UInt16 is Int32|UInt32|UInt
|
|
||||||
// * UInt32 is UInt
|
|
||||||
// * Class -> Class: if same class or A is a subclass of B
|
|
||||||
// * if type args are present, must have equal number of them
|
|
||||||
// * for each pair of type args, check variance
|
|
||||||
// * invariant: A_i must be identical to B_i
|
|
||||||
// * covariant: A_i must be a subtype of B_i
|
|
||||||
// * contravariant: B_i must be a subtype of A_i
|
|
||||||
// * Union -> Union: Each elem of A must be a subtype of at least one elem of B
|
|
||||||
// * Non-union -> Union: A must be a subtype of at least one elem of B
|
|
||||||
if (a == b) return true;
|
|
||||||
|
|
||||||
if (a instanceof PType.StringLiteral aStr) {
|
|
||||||
if (b instanceof PType.StringLiteral bStr) {
|
|
||||||
return aStr.getLiteral().equals(bStr.getLiteral());
|
|
||||||
} else if (b instanceof PType.Class bClass) {
|
|
||||||
return bClass.getPClass() == BaseModule.getStringClass().export();
|
|
||||||
}
|
|
||||||
} else if (a instanceof PType.Alias aAlias) {
|
|
||||||
var aa = aAlias.getTypeAlias();
|
|
||||||
if (isIntTypeAlias(aa)) {
|
|
||||||
// special casing for stdlib Int typealiases
|
|
||||||
if (b instanceof PType.Class bClass) {
|
|
||||||
// A is an int alias, B is a Number (sub)class
|
|
||||||
return bClass.getPClass().isSubclassOf(BaseModule.getNumberClass().export());
|
|
||||||
} else if (b instanceof PType.Alias bAlias) {
|
|
||||||
var bb = bAlias.getTypeAlias();
|
|
||||||
if (aa == bb) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (aa == BaseModule.getInt8TypeAlias().export()) {
|
|
||||||
return bb == BaseModule.getInt16TypeAlias().export()
|
|
||||||
|| bb == BaseModule.getInt32TypeAlias().export();
|
|
||||||
} else if (aa == BaseModule.getInt16TypeAlias().export()) {
|
|
||||||
return bb == BaseModule.getInt32TypeAlias().export();
|
|
||||||
} else if (aa == BaseModule.getUInt8TypeAlias().export()) {
|
|
||||||
return bb == BaseModule.getInt16TypeAlias().export()
|
|
||||||
|| bb == BaseModule.getInt32TypeAlias().export()
|
|
||||||
|| bb == BaseModule.getUInt16TypeAlias().export()
|
|
||||||
|| bb == BaseModule.getUInt32TypeAlias().export()
|
|
||||||
|| bb == BaseModule.getUIntTypeAlias().export();
|
|
||||||
} else if (aa == BaseModule.getUInt16TypeAlias().export()) {
|
|
||||||
return bb == BaseModule.getInt32TypeAlias().export()
|
|
||||||
|| bb == BaseModule.getUInt32TypeAlias().export()
|
|
||||||
|| bb == BaseModule.getUIntTypeAlias().export();
|
|
||||||
} else if (aa == BaseModule.getUInt32TypeAlias().export()) {
|
|
||||||
return bb == BaseModule.getUIntTypeAlias().export();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (a instanceof PType.Class aClass && b instanceof PType.Class bClass) {
|
|
||||||
if (!aClass.getPClass().isSubclassOf(bClass.getPClass())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
var aArgs = aClass.getTypeArguments();
|
|
||||||
var bArgs = bClass.getTypeArguments();
|
|
||||||
var bParams = bClass.getPClass().getTypeParameters();
|
|
||||||
if (aArgs.size() != bArgs.size()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// check variance of type args pairwise
|
|
||||||
for (var i = 0; i < aArgs.size(); i++) {
|
|
||||||
if (!switch (bParams.get(i).getVariance()) {
|
|
||||||
case INVARIANT -> aArgs.get(i) == bArgs.get(i);
|
|
||||||
case COVARIANT -> isSubtype(aArgs.get(i), bArgs.get(i));
|
|
||||||
case CONTRAVARIANT -> isSubtype(bArgs.get(i), aArgs.get(i));
|
|
||||||
}) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} else if (b instanceof PType.Union bUnion) {
|
|
||||||
if (a instanceof PType.Union aUnion) {
|
|
||||||
a:
|
|
||||||
for (var aElem : aUnion.getElementTypes()) {
|
|
||||||
for (var bElem : bUnion.getElementTypes()) {
|
|
||||||
if (isSubtype(aElem, bElem)) continue a;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
for (var bElem : bUnion.getElementTypes()) {
|
|
||||||
if (isSubtype(a, bElem)) return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isIntTypeAlias(TypeAlias t) {
|
private static boolean isPreservedTypeAlias(VmTypeAlias t) {
|
||||||
return t == BaseModule.getInt8TypeAlias().export()
|
|
||||||
|| t == BaseModule.getInt16TypeAlias().export()
|
|
||||||
|| t == BaseModule.getInt32TypeAlias().export()
|
|
||||||
|| t == BaseModule.getUInt8TypeAlias().export()
|
|
||||||
|| t == BaseModule.getUInt16TypeAlias().export()
|
|
||||||
|| t == BaseModule.getUInt32TypeAlias().export()
|
|
||||||
|| t == BaseModule.getUIntTypeAlias().export();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isPreservedTypeAlias(TypeAlias t) {
|
|
||||||
return isIntTypeAlias(t);
|
return isIntTypeAlias(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -494,13 +392,14 @@ public final class VmReference extends VmValue {
|
|||||||
pathList.add(elem.export());
|
pathList.add(elem.export());
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Reference(domain.export(), VmValue.export(data), pathList, getReferentType());
|
return new Reference(
|
||||||
|
domain.export(), VmValue.export(data), pathList, getReferentType().export());
|
||||||
}
|
}
|
||||||
|
|
||||||
public PType exportType() {
|
public VmType getType() {
|
||||||
return new PType.Class(
|
return new VmType.ClassType(
|
||||||
RefModule.getReferenceClass().export(),
|
RefModule.getReferenceClass(),
|
||||||
new PType.Class(domain.getVmClass().export()),
|
new VmType.ClassType(domain.getVmClass()),
|
||||||
getReferentType());
|
getReferentType());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -554,15 +453,15 @@ public final class VmReference extends VmValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static final class VmReferenceAccessError extends RuntimeException {
|
public static final class VmReferenceAccessError extends RuntimeException {
|
||||||
private final PType type;
|
private final VmType type;
|
||||||
private final VmReferenceAccessErrorType errorType;
|
private final VmReferenceAccessErrorType errorType;
|
||||||
|
|
||||||
public VmReferenceAccessError(PType type, VmReferenceAccessErrorType errorType) {
|
public VmReferenceAccessError(VmType type, VmReferenceAccessErrorType errorType) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.errorType = errorType;
|
this.errorType = errorType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PType getType() {
|
public VmType getType() {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,767 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.pkl.core.runtime;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
import org.pkl.core.PType;
|
||||||
|
import org.pkl.core.PType.Alias;
|
||||||
|
import org.pkl.core.PType.Constrained;
|
||||||
|
import org.pkl.core.PType.StringLiteral;
|
||||||
|
import org.pkl.core.PType.TypeVariable;
|
||||||
|
import org.pkl.core.PType.Union;
|
||||||
|
import org.pkl.core.TypeParameter;
|
||||||
|
import org.pkl.core.ValueFormatter;
|
||||||
|
|
||||||
|
public abstract sealed class VmType {
|
||||||
|
|
||||||
|
public abstract PType export();
|
||||||
|
|
||||||
|
/** Checks if two types are equivalent. {@code other} will never be an {@link AliasType}. */
|
||||||
|
protected abstract boolean doIsEquivalentTo(VmType other);
|
||||||
|
|
||||||
|
/** Checks if this type is a supertype of {@code other}. */
|
||||||
|
protected abstract boolean doIsSupertypeOf(VmType other);
|
||||||
|
|
||||||
|
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||||
|
public boolean isParametric() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable VmClass getVmClass() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Tells if this type is the same typecheck as the other type. */
|
||||||
|
@Override
|
||||||
|
public final boolean equals(@Nullable Object value) {
|
||||||
|
if (this == value) return true;
|
||||||
|
if (!(value instanceof VmType other)) return false;
|
||||||
|
while (other instanceof AliasType at) {
|
||||||
|
other = at.aliasedType;
|
||||||
|
}
|
||||||
|
return doIsEquivalentTo(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final boolean isSubtypeOf(VmType other) {
|
||||||
|
return other.isSupertypeOf(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final boolean isSupertypeOf(VmType other) {
|
||||||
|
return equals(other)
|
||||||
|
|| doIsSupertypeOf(other)
|
||||||
|
|| other == UnknownType.INSTANCE // all types are supertypes/subtypes of unknown
|
||||||
|
|| other == NothingType.INSTANCE
|
||||||
|
|| other instanceof TypeVariableType
|
||||||
|
|| (other instanceof UnionType ut && ut.allElementsMatch(this::isSupertypeOf))
|
||||||
|
|| (other instanceof AliasType at && isSupertypeOf(at.aliasedType))
|
||||||
|
|| (other instanceof ConstrainedType ct && isSupertypeOf(ct.baseType));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class UnknownType extends VmType {
|
||||||
|
public static final UnknownType INSTANCE = new UnknownType();
|
||||||
|
|
||||||
|
private UnknownType() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean doIsEquivalentTo(VmType other) {
|
||||||
|
// equality checked by equals()
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean doIsSupertypeOf(VmType other) {
|
||||||
|
// unknown is supertype of everything!
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PType export() {
|
||||||
|
return PType.UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class NothingType extends VmType {
|
||||||
|
public static final NothingType INSTANCE = new NothingType();
|
||||||
|
|
||||||
|
private NothingType() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean doIsEquivalentTo(VmType other) {
|
||||||
|
// equality checked by equals()
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean doIsSupertypeOf(VmType other) {
|
||||||
|
// nothing is a supertype of nothing (except itself and unknown, handled by callers)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PType export() {
|
||||||
|
return PType.NOTHING;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "nothing";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class StringLiteralType extends VmType {
|
||||||
|
private final String literal;
|
||||||
|
|
||||||
|
public StringLiteralType(String literal) {
|
||||||
|
super();
|
||||||
|
this.literal = literal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLiteral() {
|
||||||
|
return literal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PType export() {
|
||||||
|
return new StringLiteral(literal);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean doIsEquivalentTo(VmType other) {
|
||||||
|
return other instanceof StringLiteralType t && literal.equals(t.literal);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean doIsSupertypeOf(VmType other) {
|
||||||
|
// equivalence handled by caller
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return ValueFormatter.basic().formatStringValue(literal, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return 31 * literal.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract static sealed class AbstractClassType extends VmType {
|
||||||
|
protected final VmClass clazz;
|
||||||
|
protected final VmType[] typeArguments;
|
||||||
|
|
||||||
|
protected AbstractClassType(VmClass clazz, VmType[] typeArguments) {
|
||||||
|
this.clazz = clazz;
|
||||||
|
this.typeArguments = typeArguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final VmType[] getTypeArguments() {
|
||||||
|
return typeArguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final VmClass getVmClass() {
|
||||||
|
return clazz;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean isParametric() {
|
||||||
|
return typeArguments.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final boolean isFunctionNClass() {
|
||||||
|
return clazz.isFunctionNClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final boolean doIsEquivalentTo(VmType other) {
|
||||||
|
if (!(other instanceof AbstractClassType t)) return false;
|
||||||
|
if (clazz != t.clazz) return false;
|
||||||
|
return typesEquals(typeArguments, t.typeArguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final boolean doIsSupertypeOf(VmType other) {
|
||||||
|
// special case: Any is a supertype of everything
|
||||||
|
if (clazz == BaseModule.getAnyClass()) return true;
|
||||||
|
// special case: String is a supertype of all string literals and also Char
|
||||||
|
if (clazz == BaseModule.getStringClass()
|
||||||
|
&& (other instanceof StringLiteralType
|
||||||
|
|| (other instanceof AliasType at && at.typeAlias == BaseModule.getCharTypeAlias())))
|
||||||
|
return true;
|
||||||
|
// special case: Int is a supertype of range-constrained aliases
|
||||||
|
if ((clazz == BaseModule.getIntClass() || clazz == BaseModule.getNumberClass())
|
||||||
|
&& other instanceof AliasType at
|
||||||
|
&& (at.typeAlias == BaseModule.getInt8TypeAlias()
|
||||||
|
|| at.typeAlias == BaseModule.getInt16TypeAlias()
|
||||||
|
|| at.typeAlias == BaseModule.getInt32TypeAlias()
|
||||||
|
|| at.typeAlias == BaseModule.getUIntTypeAlias()
|
||||||
|
|| at.typeAlias == BaseModule.getUInt8TypeAlias()
|
||||||
|
|| at.typeAlias == BaseModule.getUInt16TypeAlias()
|
||||||
|
|| at.typeAlias == BaseModule.getUInt32TypeAlias())) return true;
|
||||||
|
// special case: class types are the supertype of non-final self types "rooted" to a subclass
|
||||||
|
if (other instanceof NonFinalSelfType st) {
|
||||||
|
return clazz.isSuperclassOf(st.clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
// standard case: other is a AbstractClassType
|
||||||
|
if (!(other instanceof AbstractClassType ct)) return false;
|
||||||
|
// if clazz isn't a superclass of other's we're not a supertype
|
||||||
|
if (!clazz.isSuperclassOf(ct.clazz)) return false;
|
||||||
|
// if our class has no type params, we are a supertype
|
||||||
|
if (clazz.getTypeParameterCount() == 0) return true;
|
||||||
|
|
||||||
|
// check generic type args: walk ct.clazz to clazz, substituting type arguments as we go
|
||||||
|
// handles arbitrary generics like Function2<A, B, R> -> Function<R>
|
||||||
|
|
||||||
|
var goalState =
|
||||||
|
typeArguments.length > 0
|
||||||
|
? typeArguments
|
||||||
|
: nCopies(clazz.getTypeParameterCount(), ClassType.ANY);
|
||||||
|
var state =
|
||||||
|
ct.typeArguments.length > 0
|
||||||
|
? ct.typeArguments
|
||||||
|
: nCopies(ct.clazz.getTypeParameterCount(), ClassType.ANY);
|
||||||
|
for (var c = ct.clazz; c != clazz; c = c.getSuperclass()) {
|
||||||
|
assert c != null; // we know walking parents reaches clazz before null
|
||||||
|
var cSuperclass = c.getSuperclass();
|
||||||
|
assert cSuperclass != null; // we know c has a superclass
|
||||||
|
var cSupertype = (AbstractClassType) c.getSupertype();
|
||||||
|
assert cSupertype != null; // we know c has a supertype
|
||||||
|
|
||||||
|
if (cSupertype.typeArguments.length == 0) {
|
||||||
|
// supertype args could be omitted, e.g. class MyList<T> extends List
|
||||||
|
state = nCopies(cSuperclass.getTypeParameterCount(), ClassType.ANY);
|
||||||
|
continue;
|
||||||
|
} else if (state.length == 0) {
|
||||||
|
// subclass may not have type args, e.g. class A extends B<Int>
|
||||||
|
state = cSupertype.typeArguments;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise, clone supertype args and substitute any type vars
|
||||||
|
var newState = cSupertype.typeArguments.clone();
|
||||||
|
for (var i = 0; i < newState.length; i++) {
|
||||||
|
if (!(newState[i] instanceof TypeVariableType tvt)) continue;
|
||||||
|
newState[i] = state[tvt.typeParameter.getIndex()];
|
||||||
|
}
|
||||||
|
state = newState;
|
||||||
|
}
|
||||||
|
|
||||||
|
// do variance checking
|
||||||
|
assert goalState.length == state.length;
|
||||||
|
var params = clazz.getTypeParameters();
|
||||||
|
for (var i = 0; i < goalState.length; i++) {
|
||||||
|
if (!switch (params.get(i).getVariance()) {
|
||||||
|
case INVARIANT -> state[i].equals(goalState[i]);
|
||||||
|
case COVARIANT -> state[i].isSubtypeOf(goalState[i]);
|
||||||
|
case CONTRAVARIANT -> state[i].isSupertypeOf(goalState[i]);
|
||||||
|
}) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int hashCode() {
|
||||||
|
return 31 * clazz.hashCode() + Arrays.hashCode(typeArguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class ClassType extends AbstractClassType {
|
||||||
|
public ClassType(VmClass clazz) {
|
||||||
|
super(clazz, new VmType[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClassType(VmClass clazz, VmType typeArgument) {
|
||||||
|
super(clazz, new VmType[] {typeArgument});
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClassType(VmClass clazz, VmType typeArgument1, VmType typeArgument2) {
|
||||||
|
super(clazz, new VmType[] {typeArgument1, typeArgument2});
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClassType(VmClass clazz, VmType[] typeArguments) {
|
||||||
|
super(clazz, typeArguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final ClassType ANY = new ClassType(BaseModule.getAnyClass());
|
||||||
|
|
||||||
|
public VmType.ClassType withTypeArguments(VmType[] typeArguments) {
|
||||||
|
return new VmType.ClassType(clazz, typeArguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PType export() {
|
||||||
|
return typeArguments.length > 0 && clazz.isFunctionNClass()
|
||||||
|
? new PType.Function(
|
||||||
|
exportTypes(typeArguments, 1), typeArguments[typeArguments.length - 1].export())
|
||||||
|
: new PType.Class(clazz.export(), exportTypes(typeArguments));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (typeArguments.length > 0 && clazz.isFunctionNClass()) {
|
||||||
|
var paramCount = typeArguments.length - 1;
|
||||||
|
var sb = new StringBuilder("(");
|
||||||
|
for (var i = 0; i < paramCount; i++) {
|
||||||
|
sb.append(typeArguments[i]);
|
||||||
|
if (i < paramCount - 1) {
|
||||||
|
sb.append(", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.append(") -> ");
|
||||||
|
sb.append(typeArguments[typeArguments.length - 1]);
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeArguments.length == 0) return clazz.getDisplayName();
|
||||||
|
return appendTypeArguments(new StringBuilder(clazz.getDisplayName()), typeArguments)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class FinalModuleType extends AbstractClassType {
|
||||||
|
public FinalModuleType(VmClass clazz) {
|
||||||
|
super(clazz, new VmType[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PType export() {
|
||||||
|
return PType.MODULE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "module";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class FinalThisType extends AbstractClassType {
|
||||||
|
public FinalThisType(VmClass clazz) {
|
||||||
|
super(clazz, new VmType[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PType export() {
|
||||||
|
return PType.THIS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "this";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private abstract static sealed class NonFinalSelfType extends VmType {
|
||||||
|
private final VmClass clazz;
|
||||||
|
|
||||||
|
protected NonFinalSelfType(VmClass clazz) {
|
||||||
|
this.clazz = clazz;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final VmClass getVmClass() {
|
||||||
|
return clazz;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final boolean doIsEquivalentTo(VmType other) {
|
||||||
|
return other instanceof NonFinalSelfType st && clazz == st.clazz;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final boolean doIsSupertypeOf(VmType other) {
|
||||||
|
return other instanceof NonFinalSelfType st && clazz.isSuperclassOf(st.clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int hashCode() {
|
||||||
|
return 31 * clazz.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class NonFinalModuleType extends NonFinalSelfType {
|
||||||
|
public NonFinalModuleType(VmClass clazz) {
|
||||||
|
super(clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PType export() {
|
||||||
|
return PType.MODULE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "module";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class NonFinalThisType extends NonFinalSelfType {
|
||||||
|
public NonFinalThisType(VmClass clazz) {
|
||||||
|
super(clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PType export() {
|
||||||
|
return PType.THIS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "this";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class NullableType extends VmType {
|
||||||
|
private final VmType elementType;
|
||||||
|
|
||||||
|
public NullableType(VmType elementType) {
|
||||||
|
this.elementType = elementType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VmType getElementType() {
|
||||||
|
return elementType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean doIsEquivalentTo(VmType other) {
|
||||||
|
return other instanceof NullableType t && elementType.equals(t.elementType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean doIsSupertypeOf(VmType other) {
|
||||||
|
if (other instanceof NullableType nt) return elementType.isSupertypeOf(nt.elementType);
|
||||||
|
return elementType.isSupertypeOf(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PType export() {
|
||||||
|
return new PType.Nullable(elementType.export());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return (elementType instanceof ClassType ct && ct.isFunctionNClass())
|
||||||
|
|| elementType instanceof UnionType
|
||||||
|
? "(" + elementType + ")?"
|
||||||
|
: elementType + "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return 31 * elementType.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class ConstrainedType extends VmType {
|
||||||
|
private final VmType baseType;
|
||||||
|
private final String[] constraints;
|
||||||
|
private final int identity;
|
||||||
|
|
||||||
|
public ConstrainedType(VmType baseType, String[] constraints, int identity) {
|
||||||
|
this.baseType = baseType;
|
||||||
|
this.constraints = constraints;
|
||||||
|
this.identity = identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VmType getBaseType() {
|
||||||
|
return baseType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean doIsEquivalentTo(VmType other) {
|
||||||
|
// consider constrained equivalent only on identity
|
||||||
|
return other instanceof ConstrainedType t && t.identity == identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean doIsSupertypeOf(VmType other) {
|
||||||
|
// constrained types can never be supertypes (except when equal by identity, handled above)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PType export() {
|
||||||
|
return new Constrained(baseType.export(), Arrays.asList(constraints));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return ((baseType instanceof ClassType ct && ct.isFunctionNClass())
|
||||||
|
|| baseType instanceof UnionType
|
||||||
|
? "(" + baseType + ")"
|
||||||
|
: baseType)
|
||||||
|
+ "("
|
||||||
|
+ String.join(", ", constraints)
|
||||||
|
+ ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return 31 * identity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class AliasType extends VmType {
|
||||||
|
private final VmTypeAlias typeAlias;
|
||||||
|
private final @Nullable VmClass clazz;
|
||||||
|
private final VmType[] typeArguments;
|
||||||
|
private final VmType aliasedType;
|
||||||
|
|
||||||
|
/** For intrinsified typealiases with an undetermined class (e.g. NonNull) */
|
||||||
|
public AliasType(VmTypeAlias typeAlias) {
|
||||||
|
this.typeAlias = typeAlias;
|
||||||
|
this.aliasedType = typeAlias.getTypeNode().getType();
|
||||||
|
this.typeArguments = new VmType[0];
|
||||||
|
this.clazz = aliasedType.getVmClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** For intrinsified typealiases with an inherent class (e.g. Int aliases) */
|
||||||
|
public AliasType(VmTypeAlias typeAlias, VmClass clazz) {
|
||||||
|
this.typeAlias = typeAlias;
|
||||||
|
this.aliasedType = typeAlias.getTypeNode().getType();
|
||||||
|
this.typeArguments = new VmType[0];
|
||||||
|
this.clazz = clazz;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** For arbitrary typealiases */
|
||||||
|
public AliasType(VmTypeAlias typeAlias, VmType[] typeArguments, VmType aliasedType) {
|
||||||
|
this.typeAlias = typeAlias;
|
||||||
|
this.typeArguments = typeArguments;
|
||||||
|
this.clazz = aliasedType.getVmClass();
|
||||||
|
this.aliasedType = aliasedType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VmTypeAlias getVmTypeAlias() {
|
||||||
|
return typeAlias;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VmType getAliasedType() {
|
||||||
|
return aliasedType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable VmClass getVmClass() {
|
||||||
|
return clazz;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean doIsEquivalentTo(VmType other) {
|
||||||
|
return aliasedType.equals(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean doIsSupertypeOf(VmType other) {
|
||||||
|
if (other instanceof AliasType at) {
|
||||||
|
if (at.typeAlias == BaseModule.getInt8TypeAlias()
|
||||||
|
&& (typeAlias == BaseModule.getInt16TypeAlias()
|
||||||
|
|| typeAlias == BaseModule.getInt32TypeAlias())) return true;
|
||||||
|
if (at.typeAlias == BaseModule.getInt16TypeAlias()
|
||||||
|
&& typeAlias == BaseModule.getInt32TypeAlias()) return true;
|
||||||
|
if (at.typeAlias == BaseModule.getUInt8TypeAlias()
|
||||||
|
&& (typeAlias == BaseModule.getInt16TypeAlias()
|
||||||
|
|| typeAlias == BaseModule.getInt32TypeAlias()
|
||||||
|
|| typeAlias == BaseModule.getUInt16TypeAlias()
|
||||||
|
|| typeAlias == BaseModule.getUInt32TypeAlias()
|
||||||
|
|| typeAlias == BaseModule.getUIntTypeAlias())) return true;
|
||||||
|
if (at.typeAlias == BaseModule.getUInt16TypeAlias()
|
||||||
|
&& (typeAlias == BaseModule.getInt32TypeAlias()
|
||||||
|
|| typeAlias == BaseModule.getUInt32TypeAlias()
|
||||||
|
|| typeAlias == BaseModule.getUIntTypeAlias())) return true;
|
||||||
|
if (at.typeAlias == BaseModule.getUInt32TypeAlias()
|
||||||
|
&& typeAlias == BaseModule.getUIntTypeAlias()) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return aliasedType.isSupertypeOf(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PType export() {
|
||||||
|
return new Alias(typeAlias.export(), exportTypes(typeArguments), aliasedType.export());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isParametric() {
|
||||||
|
return typeArguments.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (typeArguments.length == 0) return typeAlias.getDisplayName();
|
||||||
|
return appendTypeArguments(new StringBuilder(typeAlias.getDisplayName()), typeArguments)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return aliasedType.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class UnionType extends VmType {
|
||||||
|
private final int defaultIndex;
|
||||||
|
private final VmType[] elementTypes;
|
||||||
|
|
||||||
|
public UnionType(int defaultIndex, VmType[] elementTypes) {
|
||||||
|
this.defaultIndex = defaultIndex;
|
||||||
|
this.elementTypes = elementTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UnionType(int defaultIndex, String[] stringLiterals) {
|
||||||
|
this.defaultIndex = defaultIndex;
|
||||||
|
elementTypes = new VmType[stringLiterals.length];
|
||||||
|
for (var i = 0; i < elementTypes.length; i++) {
|
||||||
|
elementTypes[i] = new StringLiteralType(stringLiterals[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDefaultIndex() {
|
||||||
|
return defaultIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VmType[] getElementTypes() {
|
||||||
|
return elementTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PType export() {
|
||||||
|
return new Union(exportTypes(elementTypes));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean doIsEquivalentTo(VmType other) {
|
||||||
|
if (!(other instanceof UnionType t)) return false;
|
||||||
|
// TODO: handle A | B as equivalent to B | A
|
||||||
|
return typesEquals(elementTypes, t.elementTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean doIsSupertypeOf(VmType other) {
|
||||||
|
for (var member : elementTypes) {
|
||||||
|
if (member.isSupertypeOf(other)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUnionOfStringLiterals() {
|
||||||
|
return allElementsMatch(it -> it instanceof StringLiteralType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
for (var i = 0; i < elementTypes.length; i++) {
|
||||||
|
sb.append(elementTypes[i]);
|
||||||
|
if (i < elementTypes.length - 1) {
|
||||||
|
sb.append(" | ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Arrays.hashCode(elementTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean allElementsMatch(Function<VmType, Boolean> predicate) {
|
||||||
|
for (var member : elementTypes) {
|
||||||
|
if (!predicate.apply(member)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class TypeVariableType extends VmType {
|
||||||
|
private final TypeParameter typeParameter;
|
||||||
|
|
||||||
|
public TypeVariableType(TypeParameter typeParameter) {
|
||||||
|
this.typeParameter = typeParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TypeParameter getTypeParameter() {
|
||||||
|
return typeParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean doIsEquivalentTo(VmType other) {
|
||||||
|
return other instanceof TypeVariableType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean doIsSupertypeOf(VmType other) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PType export() {
|
||||||
|
return new TypeVariable(typeParameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return typeParameter.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return -318019;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean typesEquals(VmType[] a, VmType[] b) {
|
||||||
|
if (a.length != b.length) return false;
|
||||||
|
for (var i = 0; i < a.length; i++) {
|
||||||
|
if (!a[i].equals(b[i])) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<PType> exportTypes(VmType[] types) {
|
||||||
|
return Arrays.stream(types).map(VmType::export).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<PType> exportTypes(VmType[] types, int drop) {
|
||||||
|
return Arrays.stream(types).limit(types.length - drop).map(VmType::export).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static VmType[] nCopies(int len, VmType type) {
|
||||||
|
var ret = new VmType[len];
|
||||||
|
Arrays.fill(ret, type);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static StringBuilder appendTypeArguments(StringBuilder sb, VmType[] typeArguments) {
|
||||||
|
sb.append('<');
|
||||||
|
for (var i = 0; i < typeArguments.length; i++) {
|
||||||
|
sb.append(typeArguments[i]);
|
||||||
|
if (i < typeArguments.length - 1) {
|
||||||
|
sb.append(", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.append('>');
|
||||||
|
return sb;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -137,6 +137,10 @@ public final class VmTypeAlias extends VmValue {
|
|||||||
return annotations;
|
return annotations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public VmClass getModuleClass() {
|
||||||
|
return module.getVmClass();
|
||||||
|
}
|
||||||
|
|
||||||
public String getModuleName() {
|
public String getModuleName() {
|
||||||
return module.getVmClass().getModuleName();
|
return module.getVmClass().getModuleName();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import com.oracle.truffle.api.dsl.TypeSystem;
|
|||||||
long.class,
|
long.class,
|
||||||
double.class,
|
double.class,
|
||||||
boolean.class,
|
boolean.class,
|
||||||
|
VmType.class,
|
||||||
VmDuration.class,
|
VmDuration.class,
|
||||||
VmDataSize.class,
|
VmDataSize.class,
|
||||||
VmIntSeq.class,
|
VmIntSeq.class,
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ import org.pkl.core.runtime.VmPair;
|
|||||||
import org.pkl.core.runtime.VmReference;
|
import org.pkl.core.runtime.VmReference;
|
||||||
import org.pkl.core.runtime.VmRegex;
|
import org.pkl.core.runtime.VmRegex;
|
||||||
import org.pkl.core.runtime.VmSet;
|
import org.pkl.core.runtime.VmSet;
|
||||||
|
import org.pkl.core.runtime.VmType;
|
||||||
import org.pkl.core.runtime.VmTyped;
|
import org.pkl.core.runtime.VmTyped;
|
||||||
import org.pkl.core.runtime.VmUtils;
|
import org.pkl.core.runtime.VmUtils;
|
||||||
import org.pkl.core.runtime.VmValue;
|
import org.pkl.core.runtime.VmValue;
|
||||||
@@ -118,7 +119,9 @@ public final class RendererNodes {
|
|||||||
var clazz =
|
var clazz =
|
||||||
value instanceof VmValue vmValue
|
value instanceof VmValue vmValue
|
||||||
? vmValue.getVmClass()
|
? vmValue.getVmClass()
|
||||||
: value instanceof TypeNode typeNode ? typeNode.getVmClass() : value.getClass();
|
: value instanceof TypeNode typeNode
|
||||||
|
? typeNode.getType().getVmClass()
|
||||||
|
: value.getClass();
|
||||||
if (clazz == null) {
|
if (clazz == null) {
|
||||||
throw new VmExceptionBuilder()
|
throw new VmExceptionBuilder()
|
||||||
.evalError("cannotResolveTypeForProtobuf")
|
.evalError("cannotResolveTypeForProtobuf")
|
||||||
@@ -345,7 +348,7 @@ public final class RendererNodes {
|
|||||||
return computedName;
|
return computedName;
|
||||||
}
|
}
|
||||||
} else if (type instanceof NonFinalClassTypeNode) {
|
} else if (type instanceof NonFinalClassTypeNode) {
|
||||||
var clazz = type.getVmClass();
|
var clazz = type.getType().getVmClass();
|
||||||
if (value instanceof VmValue vmValue) {
|
if (value instanceof VmValue vmValue) {
|
||||||
if (clazz == vmValue.getVmClass()) {
|
if (clazz == vmValue.getVmClass()) {
|
||||||
return clazz.getSimpleName();
|
return clazz.getSimpleName();
|
||||||
@@ -402,9 +405,9 @@ public final class RendererNodes {
|
|||||||
hasCollection = true;
|
hasCollection = true;
|
||||||
collectionType = setType.getElementTypeNode();
|
collectionType = setType.getElementTypeNode();
|
||||||
} else if (type instanceof NonFinalClassTypeNode) {
|
} else if (type instanceof NonFinalClassTypeNode) {
|
||||||
if (type.getVmClass() != clazz) {
|
if (type.getType().getVmClass() != clazz) {
|
||||||
throw new VmExceptionBuilder()
|
throw new VmExceptionBuilder()
|
||||||
.evalError("cannotRenderSubtypeForProtobuf", type.getVmClass(), clazz)
|
.evalError("cannotRenderSubtypeForProtobuf", type.getType().getVmClass(), clazz)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -581,7 +584,8 @@ public final class RendererNodes {
|
|||||||
} else if (type instanceof NullableTypeNode nullableType) {
|
} else if (type instanceof NullableTypeNode nullableType) {
|
||||||
return resolveType(nullableType.getElementTypeNode());
|
return resolveType(nullableType.getElementTypeNode());
|
||||||
} else if (type instanceof TypeAliasTypeNode typeAliasType) {
|
} else if (type instanceof TypeAliasTypeNode typeAliasType) {
|
||||||
return resolveType(typeAliasType.getVmTypeAlias().getTypeNode());
|
return resolveType(
|
||||||
|
((VmType.AliasType) typeAliasType.getType()).getVmTypeAlias().getTypeNode());
|
||||||
} else if (type instanceof ListingTypeNode listingType) {
|
} else if (type instanceof ListingTypeNode listingType) {
|
||||||
var valueType = resolveType(listingType.getValueTypeNode());
|
var valueType = resolveType(listingType.getValueTypeNode());
|
||||||
assert valueType != null : "Failed to resolve type node.";
|
assert valueType != null : "Failed to resolve type node.";
|
||||||
@@ -633,8 +637,8 @@ public final class RendererNodes {
|
|||||||
elements.sort(
|
elements.sort(
|
||||||
(o1, o2) -> {
|
(o1, o2) -> {
|
||||||
if (o1 instanceof ObjectSlotTypeNode && o2 instanceof ObjectSlotTypeNode) {
|
if (o1 instanceof ObjectSlotTypeNode && o2 instanceof ObjectSlotTypeNode) {
|
||||||
var t1 = o1.getVmClass();
|
var t1 = o1.getType().getVmClass();
|
||||||
var t2 = o2.getVmClass();
|
var t2 = o2.getType().getVmClass();
|
||||||
if (t1 == null || t2 == null) return 0;
|
if (t1 == null || t2 == null) return 0;
|
||||||
return t1.isSubclassOf(t2) ? -1 : t2.isSubclassOf(t1) ? 1 : 0;
|
return t1.isSubclassOf(t2) ? -1 : t2.isSubclassOf(t1) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -359,7 +359,7 @@ typeMismatchBecause=\
|
|||||||
* Value is not of type `{0}` because:
|
* Value is not of type `{0}` because:
|
||||||
|
|
||||||
classTypeMismatchHint=\
|
classTypeMismatchHint=\
|
||||||
A `Class` type check can only succeed when its type argument is an un-parameterized class, `unknown`, `module`, or an alias to one of those types.
|
A `Class` type check can only succeed when its type argument is an un-parameterized class, `unknown`, `module`, `this`, or an alias to one of those types.
|
||||||
|
|
||||||
typeConstraintViolated=\
|
typeConstraintViolated=\
|
||||||
Type constraint `{0}` violated.
|
Type constraint `{0}` violated.
|
||||||
|
|||||||
@@ -134,6 +134,9 @@ class BadResource extends Resource {
|
|||||||
|
|
||||||
badThisRef = test.catch(() -> new BadResource { name = "bad" }.$.toString())
|
badThisRef = test.catch(() -> new BadResource { name = "bad" }.$.toString())
|
||||||
|
|
||||||
|
// test that a bare function type toStrings/exports okay
|
||||||
|
bareFunctionType = test.catch(() -> ref.Reference(d, Function4, null) as Ref<String>)
|
||||||
|
|
||||||
output {
|
output {
|
||||||
renderer {
|
renderer {
|
||||||
converters {
|
converters {
|
||||||
|
|||||||
@@ -61,3 +61,4 @@ aValuesJoined = """
|
|||||||
"""
|
"""
|
||||||
typeArgs = "${null.prop}"
|
typeArgs = "${null.prop}"
|
||||||
badThisRef = "Expected value of type `pkl.ref#Reference<reference#D, this>`, but got type `pkl.ref#Reference<reference#D, reference#A>`. Value: Reference(new D {}, reference#A, new BadResource { name = ? })"
|
badThisRef = "Expected value of type `pkl.ref#Reference<reference#D, this>`, but got type `pkl.ref#Reference<reference#D, reference#A>`. Value: Reference(new D {}, reference#A, new BadResource { name = ? })"
|
||||||
|
bareFunctionType = "Expected value of type `pkl.ref#Reference<reference#D, String>`, but got type `pkl.ref#Reference<reference#D, Function4>`. Value: Reference(new D {}, Function4, null)"
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
–– Pkl Error ––
|
–– Pkl Error ––
|
||||||
Cannot find property `default` in object of type `pkl.ref#Reference<reference13#D, Mapping<unknown, unknown>>`.
|
Cannot find property `default` in object of type `pkl.ref#Reference<reference13#D, Mapping>`.
|
||||||
|
|
||||||
x | test = ref.Reference(d, Mapping, "").default
|
x | test = ref.Reference(d, Mapping, "").default
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
–– Pkl Error ––
|
–– Pkl Error ––
|
||||||
Cannot find property `default` in object of type `pkl.ref#Reference<reference14#D, Listing<unknown>>`.
|
Cannot find property `default` in object of type `pkl.ref#Reference<reference14#D, Listing>`.
|
||||||
|
|
||||||
x | test = ref.Reference(d, Listing, "").default
|
x | test = ref.Reference(d, Listing, "").default
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
–– Pkl Error ––
|
–– Pkl Error ––
|
||||||
Cannot find property `name` in object of type `pkl.ref#Reference<reference26#D, Listing<unknown>>`.
|
Cannot find property `name` in object of type `pkl.ref#Reference<reference26#D, Listing>`.
|
||||||
|
|
||||||
x | res = myRef.name
|
x | res = myRef.name
|
||||||
^^^^^^^^^^
|
^^^^^^^^^^
|
||||||
at reference26#res (file:///$snippetsDir/input/errors/reference26.pkl)
|
at reference26#res (file:///$snippetsDir/input/errors/reference26.pkl)
|
||||||
|
|
||||||
Cannot find property `name` in type `Listing<unknown>`.
|
Cannot find property `name` in type `Listing`.
|
||||||
|
|
||||||
xxx | renderer.renderDocument(value)
|
xxx | renderer.renderDocument(value)
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
–– Pkl Error ––
|
–– Pkl Error ––
|
||||||
Operator `[]` is not defined for operand types `pkl.ref#Reference<reference7#D, List<unknown>>` and `String`.
|
Operator `[]` is not defined for operand types `pkl.ref#Reference<reference7#D, List>` and `String`.
|
||||||
Left operand : Reference(new D {}, List<unknown>, List())
|
Left operand : Reference(new D {}, List, List())
|
||||||
Right operand: "hi"
|
Right operand: "hi"
|
||||||
|
|
||||||
x | test = ref.Reference(d, List, List())["hi"]
|
x | test = ref.Reference(d, List, List())["hi"]
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
at reference7#test (file:///$snippetsDir/input/errors/reference7.pkl)
|
at reference7#test (file:///$snippetsDir/input/errors/reference7.pkl)
|
||||||
|
|
||||||
Operator `[]` is not defined for operand types `List<unknown>` and `String`.
|
Operator `[]` is not defined for operand types `List` and `String`.
|
||||||
|
|
||||||
xxx | renderer.renderDocument(value)
|
xxx | renderer.renderDocument(value)
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ xx | typealias F<T> = List<Class<T>>
|
|||||||
^^^^^^^^
|
^^^^^^^^
|
||||||
at classTypeErr11#res22 (file:///$snippetsDir/input/types/classType.pkl)
|
at classTypeErr11#res22 (file:///$snippetsDir/input/types/classType.pkl)
|
||||||
|
|
||||||
A `Class` type check can only succeed when its type argument is an un-parameterized class, `unknown`, `module`, or an alias to one of those types.
|
A `Class` type check can only succeed when its type argument is an un-parameterized class, `unknown`, `module`, `this`, or an alias to one of those types.
|
||||||
|
|
||||||
xxx | renderer.renderDocument(value)
|
xxx | renderer.renderDocument(value)
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ xx | typealias F<T> = List<Class<T>>
|
|||||||
^^^^^^^^
|
^^^^^^^^
|
||||||
at classTypeErr12#res22 (file:///$snippetsDir/input/types/classType.pkl)
|
at classTypeErr12#res22 (file:///$snippetsDir/input/types/classType.pkl)
|
||||||
|
|
||||||
A `Class` type check can only succeed when its type argument is an un-parameterized class, `unknown`, `module`, or an alias to one of those types.
|
A `Class` type check can only succeed when its type argument is an un-parameterized class, `unknown`, `module`, `this`, or an alias to one of those types.
|
||||||
|
|
||||||
xxx | renderer.renderDocument(value)
|
xxx | renderer.renderDocument(value)
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ xx | typealias F<T> = List<Class<T>>
|
|||||||
^^^^^^^^
|
^^^^^^^^
|
||||||
at classTypeErr13#res22 (file:///$snippetsDir/input/types/classType.pkl)
|
at classTypeErr13#res22 (file:///$snippetsDir/input/types/classType.pkl)
|
||||||
|
|
||||||
A `Class` type check can only succeed when its type argument is an un-parameterized class, `unknown`, `module`, or an alias to one of those types.
|
A `Class` type check can only succeed when its type argument is an un-parameterized class, `unknown`, `module`, `this`, or an alias to one of those types.
|
||||||
|
|
||||||
xxx | renderer.renderDocument(value)
|
xxx | renderer.renderDocument(value)
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ x | res8 = C as Class<A | B>
|
|||||||
^^^^^^^^^^^^
|
^^^^^^^^^^^^
|
||||||
at classTypeErr4#res8 (file:///$snippetsDir/input/types/classTypeErr4.pkl)
|
at classTypeErr4#res8 (file:///$snippetsDir/input/types/classTypeErr4.pkl)
|
||||||
|
|
||||||
A `Class` type check can only succeed when its type argument is an un-parameterized class, `unknown`, `module`, or an alias to one of those types.
|
A `Class` type check can only succeed when its type argument is an un-parameterized class, `unknown`, `module`, `this`, or an alias to one of those types.
|
||||||
|
|
||||||
xxx | renderer.renderDocument(value)
|
xxx | renderer.renderDocument(value)
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ x | res9 = C as Class<A?>
|
|||||||
^^^^^^^^^
|
^^^^^^^^^
|
||||||
at classTypeErr5#res9 (file:///$snippetsDir/input/types/classTypeErr5.pkl)
|
at classTypeErr5#res9 (file:///$snippetsDir/input/types/classTypeErr5.pkl)
|
||||||
|
|
||||||
A `Class` type check can only succeed when its type argument is an un-parameterized class, `unknown`, `module`, or an alias to one of those types.
|
A `Class` type check can only succeed when its type argument is an un-parameterized class, `unknown`, `module`, `this`, or an alias to one of those types.
|
||||||
|
|
||||||
xxx | renderer.renderDocument(value)
|
xxx | renderer.renderDocument(value)
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ x | res10 = C as Class<"foo">
|
|||||||
^^^^^^^^^^^^
|
^^^^^^^^^^^^
|
||||||
at classTypeErr6#res10 (file:///$snippetsDir/input/types/classTypeErr6.pkl)
|
at classTypeErr6#res10 (file:///$snippetsDir/input/types/classTypeErr6.pkl)
|
||||||
|
|
||||||
A `Class` type check can only succeed when its type argument is an un-parameterized class, `unknown`, `module`, or an alias to one of those types.
|
A `Class` type check can only succeed when its type argument is an un-parameterized class, `unknown`, `module`, `this`, or an alias to one of those types.
|
||||||
|
|
||||||
xxx | renderer.renderDocument(value)
|
xxx | renderer.renderDocument(value)
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ x | res11 = C as Class<nothing>
|
|||||||
^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^
|
||||||
at classTypeErr7#res11 (file:///$snippetsDir/input/types/classTypeErr7.pkl)
|
at classTypeErr7#res11 (file:///$snippetsDir/input/types/classTypeErr7.pkl)
|
||||||
|
|
||||||
A `Class` type check can only succeed when its type argument is an un-parameterized class, `unknown`, `module`, or an alias to one of those types.
|
A `Class` type check can only succeed when its type argument is an un-parameterized class, `unknown`, `module`, `this`, or an alias to one of those types.
|
||||||
|
|
||||||
xxx | renderer.renderDocument(value)
|
xxx | renderer.renderDocument(value)
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ x | res12 = C as Class<A(true)>
|
|||||||
^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^
|
||||||
at classTypeErr8#res12 (file:///$snippetsDir/input/types/classTypeErr8.pkl)
|
at classTypeErr8#res12 (file:///$snippetsDir/input/types/classTypeErr8.pkl)
|
||||||
|
|
||||||
A `Class` type check can only succeed when its type argument is an un-parameterized class, `unknown`, `module`, or an alias to one of those types.
|
A `Class` type check can only succeed when its type argument is an un-parameterized class, `unknown`, `module`, `this`, or an alias to one of those types.
|
||||||
|
|
||||||
xxx | renderer.renderDocument(value)
|
xxx | renderer.renderDocument(value)
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|||||||
@@ -651,7 +651,8 @@ class CommandSpecParserTest {
|
|||||||
|
|
||||||
val exc = assertThrows<PklException> { parse(moduleUri) }
|
val exc = assertThrows<PklException> { parse(moduleUri) }
|
||||||
assertThat(exc.message).contains("foo: Foo")
|
assertThat(exc.message).contains("foo: Foo")
|
||||||
assertThat(exc.message).contains("Command option property `foo` has unsupported type `Foo`.")
|
assertThat(exc.message)
|
||||||
|
.contains("Command option property `foo` has unsupported type `cmd#Foo`.")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -805,7 +806,7 @@ class CommandSpecParserTest {
|
|||||||
val spec = parse(moduleUri)
|
val spec = parse(moduleUri)
|
||||||
val flag = spec.options.first() as CommandSpec.Flag
|
val flag = spec.options.first() as CommandSpec.Flag
|
||||||
|
|
||||||
assertThat(flag.metavar()).isEqualTo("[json, toml, yaml]")
|
assertThat(flag.metavar()).isEqualTo("[json, yaml, toml]")
|
||||||
|
|
||||||
val apply =
|
val apply =
|
||||||
assertThrows<CommandSpec.Option.BadValue> {
|
assertThrows<CommandSpec.Option.BadValue> {
|
||||||
|
|||||||
@@ -0,0 +1,450 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.pkl.core.runtime
|
||||||
|
|
||||||
|
import java.net.URI
|
||||||
|
import java.nio.file.Path
|
||||||
|
import kotlin.io.path.writeText
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.junit.jupiter.api.Disabled
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.io.TempDir
|
||||||
|
import org.pkl.core.Loggers
|
||||||
|
import org.pkl.core.SecurityManagers
|
||||||
|
import org.pkl.core.StackFrameTransformers
|
||||||
|
import org.pkl.core.TypeParameter
|
||||||
|
import org.pkl.core.evaluatorSettings.TraceMode
|
||||||
|
import org.pkl.core.http.HttpClient
|
||||||
|
import org.pkl.core.module.ModuleKey
|
||||||
|
import org.pkl.core.module.ModuleKeyFactories
|
||||||
|
import org.pkl.core.module.ModuleKeys.file
|
||||||
|
import org.pkl.core.module.ModuleKeys.synthetic
|
||||||
|
|
||||||
|
class VmTypeTest {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val stringClass = VmType.ClassType(BaseModule.getStringClass())
|
||||||
|
val boolClass = VmType.ClassType(BaseModule.getBooleanClass())
|
||||||
|
val intClass = VmType.ClassType(BaseModule.getIntClass())
|
||||||
|
val numberClass = VmType.ClassType(BaseModule.getNumberClass())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeModule(rootDir: Path?, moduleKey: ModuleKey): VmTyped {
|
||||||
|
val securityManager =
|
||||||
|
SecurityManagers.standard(
|
||||||
|
SecurityManagers.defaultAllowedModules,
|
||||||
|
SecurityManagers.defaultAllowedResources,
|
||||||
|
SecurityManagers.defaultTrustLevels,
|
||||||
|
rootDir,
|
||||||
|
)
|
||||||
|
var ret: VmTyped? = null
|
||||||
|
VmUtils.createContext {
|
||||||
|
val vmContext = VmContext.get(null)
|
||||||
|
vmContext.initialize(
|
||||||
|
VmContext.Holder(
|
||||||
|
StackFrameTransformers.defaultTransformer,
|
||||||
|
securityManager,
|
||||||
|
HttpClient.dummyClient(),
|
||||||
|
ModuleResolver(
|
||||||
|
listOfNotNull(
|
||||||
|
ModuleKeyFactories.standardLibrary,
|
||||||
|
rootDir?.let { ModuleKeyFactories.file },
|
||||||
|
)
|
||||||
|
),
|
||||||
|
ResourceManager(securityManager, listOf()),
|
||||||
|
Loggers.noop(),
|
||||||
|
mapOf(),
|
||||||
|
mapOf(),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
TraceMode.COMPACT,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
ret = VmLanguage.get(null).loadModule(moduleKey)
|
||||||
|
}
|
||||||
|
.close()
|
||||||
|
return ret!!
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeModule(text: String): VmTyped =
|
||||||
|
makeModule(null, synthetic(URI("repl:text"), text))
|
||||||
|
|
||||||
|
private fun makeModule(dir: Path, path: Path): VmTyped = makeModule(dir, file(path.toUri()))
|
||||||
|
|
||||||
|
private fun VmTyped.getTypeForProperty(property: String): VmType =
|
||||||
|
vmClass.getProperty(Identifier.get(property))!!.typeNode!!.typeNode.type
|
||||||
|
|
||||||
|
private fun VmTyped.getTypeForNestedProperty(prop1: String, prop2: String): VmType =
|
||||||
|
vmClass
|
||||||
|
.getProperty(Identifier.get(prop1))!!
|
||||||
|
.typeNode!!
|
||||||
|
.typeNode
|
||||||
|
.type
|
||||||
|
.vmClass!!
|
||||||
|
.getProperty(Identifier.get(prop2))!!
|
||||||
|
.typeNode!!
|
||||||
|
.typeNode
|
||||||
|
.type
|
||||||
|
|
||||||
|
/** Assert this is a subtype of [other] and [other] is a supertype of this, but not equivalent */
|
||||||
|
private fun VmType.sub(other: VmType) {
|
||||||
|
assertThat(this.isSubtypeOf(other)).`as`("%s isSubtypeOf %s", this, other).isTrue
|
||||||
|
assertThat(other.isSupertypeOf(this)).`as`("%s isSupertypeOf %s", other, this).isTrue
|
||||||
|
assertThat(other.isSubtypeOf(this)).`as`("%s isSubtypeOf %s", other, this).isFalse
|
||||||
|
assertThat(this.isSupertypeOf(other)).`as`("%s isSupertypeOf %s", this, other).isFalse
|
||||||
|
assertThat(this == other).`as`("%s == %s", this, other).isFalse
|
||||||
|
assertThat(other == this).`as`("%s == %s", other, this).isFalse
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert this is a subtype of [other] and [other] is a supertype of this and they are equivalent
|
||||||
|
*/
|
||||||
|
private fun VmType.eq(other: VmType) {
|
||||||
|
assertThat(this.isSubtypeOf(other)).`as`("%s isSubtypeOf %s", this, other).isTrue
|
||||||
|
assertThat(other.isSupertypeOf(this)).`as`("%s isSupertypeOf %s", other, this).isTrue
|
||||||
|
assertThat(other.isSubtypeOf(this)).`as`("%s isSubtypeOf %s", other, this).isTrue
|
||||||
|
assertThat(this.isSupertypeOf(other)).`as`("%s isSupertypeOf %s", this, other).isTrue
|
||||||
|
assertThat(this == other).`as`("%s == %s", this, other).isTrue
|
||||||
|
assertThat(other == this).`as`("%s == %s", other, this).isTrue
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Assert this is a subtype of [other] and [other] is a subtype of this, but not equivalent */
|
||||||
|
private fun VmType.bidi(other: VmType) {
|
||||||
|
assertThat(this.isSubtypeOf(other)).`as`("%s isSubtypeOf %s", this, other).isTrue
|
||||||
|
assertThat(other.isSupertypeOf(this)).`as`("%s isSupertypeOf %s", other, this).isTrue
|
||||||
|
assertThat(other.isSubtypeOf(this)).`as`("%s isSubtypeOf %s", other, this).isTrue
|
||||||
|
assertThat(this.isSupertypeOf(other)).`as`("%s isSupertypeOf %s", this, other).isTrue
|
||||||
|
assertThat(this == other).`as`("%s == %s", this, other).isFalse
|
||||||
|
assertThat(other == this).`as`("%s == %s", other, this).isFalse
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `unknown - equality (referential)`() {
|
||||||
|
VmType.UnknownType.INSTANCE.eq(VmType.UnknownType.INSTANCE)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `unknown - subtype and supertype of everything`() {
|
||||||
|
VmType.UnknownType.INSTANCE.bidi(VmType.ClassType(BaseModule.getStringClass()))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `nothing - equality (referential)`() {
|
||||||
|
VmType.NothingType.INSTANCE.eq(VmType.NothingType.INSTANCE)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `nothing - subtype of everything`() {
|
||||||
|
VmType.NothingType.INSTANCE.sub(VmType.ClassType(BaseModule.getStringClass()))
|
||||||
|
VmType.NothingType.INSTANCE.sub(VmType.StringLiteralType("foo"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `final self - equality`(@TempDir tempDir: Path) {
|
||||||
|
val modPath = tempDir.resolve("test.pkl")
|
||||||
|
modPath.writeText(
|
||||||
|
"""
|
||||||
|
module mod
|
||||||
|
import "test.pkl" as Declared
|
||||||
|
|
||||||
|
hidden moduleType: module
|
||||||
|
hidden moduleType2: module
|
||||||
|
hidden thisType: this
|
||||||
|
hidden thisType2: this
|
||||||
|
hidden declaredType: Declared
|
||||||
|
"""
|
||||||
|
.trimIndent()
|
||||||
|
)
|
||||||
|
val mod = makeModule(tempDir, modPath)
|
||||||
|
|
||||||
|
assertThat(mod.vmClass.isOpen).isFalse
|
||||||
|
mod.getTypeForProperty("moduleType").eq(mod.getTypeForProperty("moduleType2"))
|
||||||
|
mod.getTypeForProperty("moduleType").eq(mod.getTypeForProperty("thisType"))
|
||||||
|
mod.getTypeForProperty("moduleType").eq(mod.getTypeForProperty("declaredType"))
|
||||||
|
mod.getTypeForProperty("thisType").eq(mod.getTypeForProperty("thisType2"))
|
||||||
|
mod.getTypeForProperty("thisType").eq(mod.getTypeForProperty("declaredType"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `non-final self - equality`() {
|
||||||
|
val mod =
|
||||||
|
makeModule(
|
||||||
|
"""
|
||||||
|
open module mod
|
||||||
|
|
||||||
|
hidden moduleType: module
|
||||||
|
hidden moduleType2: module
|
||||||
|
hidden thisType: this
|
||||||
|
hidden thisType2: this
|
||||||
|
"""
|
||||||
|
.trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
assertThat(mod.vmClass.isOpen).isTrue
|
||||||
|
mod.getTypeForProperty("moduleType").eq(mod.getTypeForProperty("moduleType2"))
|
||||||
|
mod.getTypeForProperty("moduleType").eq(mod.getTypeForProperty("thisType"))
|
||||||
|
mod.getTypeForProperty("thisType").eq(mod.getTypeForProperty("thisType2"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `non-final self - comparison to extended type`(@TempDir tempDir: Path) {
|
||||||
|
val parentPath = tempDir.resolve("parent.pkl")
|
||||||
|
parentPath.writeText(
|
||||||
|
"""
|
||||||
|
open module parent
|
||||||
|
|
||||||
|
moduleType: module
|
||||||
|
thisType: this
|
||||||
|
"""
|
||||||
|
.trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
val modPath = tempDir.resolve("test.pkl")
|
||||||
|
modPath.writeText(
|
||||||
|
"""
|
||||||
|
open module mod
|
||||||
|
extends "parent.pkl"
|
||||||
|
import "parent.pkl" as Parent
|
||||||
|
|
||||||
|
parent: Parent
|
||||||
|
child: Child
|
||||||
|
|
||||||
|
moduleType2: module
|
||||||
|
thisType2: this
|
||||||
|
|
||||||
|
open class Child extends module {
|
||||||
|
// no moduleType3 because module type usage in class bodies is deprecated
|
||||||
|
thisType3: this
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
.trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
val mod = makeModule(tempDir, modPath)
|
||||||
|
|
||||||
|
mod.getTypeForProperty("moduleType").eq(mod.getTypeForNestedProperty("parent", "moduleType"))
|
||||||
|
mod.getTypeForProperty("thisType").eq(mod.getTypeForNestedProperty("parent", "moduleType"))
|
||||||
|
mod.getTypeForProperty("moduleType").eq(mod.getTypeForNestedProperty("parent", "thisType"))
|
||||||
|
mod.getTypeForProperty("thisType").eq(mod.getTypeForNestedProperty("parent", "thisType"))
|
||||||
|
mod.getTypeForProperty("moduleType2").sub(mod.getTypeForNestedProperty("parent", "moduleType"))
|
||||||
|
mod.getTypeForProperty("thisType2").sub(mod.getTypeForNestedProperty("parent", "moduleType"))
|
||||||
|
mod.getTypeForProperty("moduleType2").sub(mod.getTypeForNestedProperty("parent", "thisType"))
|
||||||
|
mod.getTypeForProperty("thisType2").sub(mod.getTypeForNestedProperty("parent", "thisType"))
|
||||||
|
|
||||||
|
mod
|
||||||
|
.getTypeForNestedProperty("child", "moduleType")
|
||||||
|
.eq(mod.getTypeForNestedProperty("parent", "moduleType"))
|
||||||
|
mod
|
||||||
|
.getTypeForNestedProperty("child", "thisType")
|
||||||
|
.eq(mod.getTypeForNestedProperty("parent", "moduleType"))
|
||||||
|
mod
|
||||||
|
.getTypeForNestedProperty("child", "moduleType")
|
||||||
|
.eq(mod.getTypeForNestedProperty("parent", "thisType"))
|
||||||
|
mod
|
||||||
|
.getTypeForNestedProperty("child", "thisType")
|
||||||
|
.eq(mod.getTypeForNestedProperty("parent", "thisType"))
|
||||||
|
mod
|
||||||
|
.getTypeForNestedProperty("child", "thisType3")
|
||||||
|
.sub(mod.getTypeForNestedProperty("parent", "moduleType"))
|
||||||
|
mod
|
||||||
|
.getTypeForNestedProperty("child", "thisType3")
|
||||||
|
.sub(mod.getTypeForNestedProperty("parent", "thisType"))
|
||||||
|
|
||||||
|
mod.getTypeForNestedProperty("child", "moduleType").eq(mod.getTypeForProperty("moduleType"))
|
||||||
|
mod.getTypeForNestedProperty("child", "thisType").eq(mod.getTypeForProperty("moduleType"))
|
||||||
|
mod.getTypeForNestedProperty("child", "moduleType").eq(mod.getTypeForProperty("thisType"))
|
||||||
|
mod.getTypeForNestedProperty("child", "thisType").eq(mod.getTypeForProperty("thisType"))
|
||||||
|
mod.getTypeForNestedProperty("child", "thisType3").sub(mod.getTypeForProperty("moduleType2"))
|
||||||
|
mod.getTypeForNestedProperty("child", "thisType3").sub(mod.getTypeForProperty("thisType2"))
|
||||||
|
|
||||||
|
// non-final self type subtype of declared type that is superclass of self type's "base"
|
||||||
|
// class types are never subtypes of non-final self types
|
||||||
|
mod.getTypeForProperty("moduleType").sub(mod.getTypeForProperty("parent"))
|
||||||
|
mod.getTypeForProperty("thisType").sub(mod.getTypeForProperty("parent"))
|
||||||
|
mod.getTypeForProperty("moduleType2").sub(mod.getTypeForProperty("parent"))
|
||||||
|
mod.getTypeForProperty("thisType2").sub(mod.getTypeForProperty("parent"))
|
||||||
|
mod.getTypeForNestedProperty("child", "thisType3").sub(mod.getTypeForProperty("parent"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `string literal - equality`() {
|
||||||
|
VmType.StringLiteralType("foo").eq(VmType.StringLiteralType("foo"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `string literal - subtype of String`() {
|
||||||
|
VmType.StringLiteralType("foo").sub(VmType.ClassType(BaseModule.getStringClass()))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `class - equality, unparameterized`() {
|
||||||
|
VmType.ClassType(BaseModule.getBooleanClass())
|
||||||
|
.eq(VmType.ClassType(BaseModule.getBooleanClass()))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `class - equality, unparameterized generic`() {
|
||||||
|
VmType.ClassType(BaseModule.getListClass()).eq(VmType.ClassType(BaseModule.getListClass()))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `class - equality, parameterized generic`() {
|
||||||
|
VmType.ClassType(BaseModule.getMappingClass(), stringClass, boolClass)
|
||||||
|
.eq(VmType.ClassType(BaseModule.getMappingClass(), stringClass, boolClass))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `class - subtype, unparameterized generic`() {
|
||||||
|
VmType.ClassType(BaseModule.getListClass())
|
||||||
|
.sub(VmType.ClassType(BaseModule.getCollectionClass()))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `class - subtype of any`() {
|
||||||
|
VmType.ClassType(BaseModule.getListClass()).sub(VmType.ClassType(BaseModule.getAnyClass()))
|
||||||
|
VmType.ClassType(BaseModule.getListClass(), stringClass)
|
||||||
|
.sub(VmType.ClassType(BaseModule.getAnyClass()))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `class - subtype of unparameterized`() {
|
||||||
|
VmType.ClassType(BaseModule.getListClass(), stringClass)
|
||||||
|
.sub(VmType.ClassType(BaseModule.getListClass()))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `class - subtype, generic with substitution`() {
|
||||||
|
VmType.ClassType(BaseModule.getFunction1Class(), stringClass, boolClass)
|
||||||
|
.sub(VmType.ClassType(BaseModule.getFunctionClass(), boolClass))
|
||||||
|
VmType.ClassType(BaseModule.getFunction1Class(), stringClass, intClass)
|
||||||
|
.sub(VmType.ClassType(BaseModule.getFunctionClass(), numberClass))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `class - special cases`() {
|
||||||
|
VmType.AliasType(BaseModule.getCharTypeAlias()).sub(stringClass)
|
||||||
|
for (cls in listOf(intClass, numberClass)) {
|
||||||
|
VmType.AliasType(BaseModule.getInt8TypeAlias()).sub(cls)
|
||||||
|
VmType.AliasType(BaseModule.getInt16TypeAlias()).sub(cls)
|
||||||
|
VmType.AliasType(BaseModule.getInt32TypeAlias()).sub(cls)
|
||||||
|
VmType.AliasType(BaseModule.getUIntTypeAlias()).sub(cls)
|
||||||
|
VmType.AliasType(BaseModule.getUInt8TypeAlias()).sub(cls)
|
||||||
|
VmType.AliasType(BaseModule.getUInt16TypeAlias()).sub(cls)
|
||||||
|
VmType.AliasType(BaseModule.getUInt32TypeAlias()).sub(cls)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `nullable - class is subtype of nullable class`() {
|
||||||
|
stringClass.sub(VmType.NullableType(stringClass))
|
||||||
|
intClass.sub(VmType.NullableType(numberClass))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `nullable - subtype of nullable supertype`() {
|
||||||
|
VmType.NullableType(intClass).sub(VmType.NullableType(numberClass))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `constrained - equality (identity)`() {
|
||||||
|
VmType.ConstrainedType(stringClass, arrayOf("true"), 0)
|
||||||
|
.eq(VmType.ConstrainedType(stringClass, arrayOf("true"), 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `constrained - subtype when base type is supertype`() {
|
||||||
|
VmType.ConstrainedType(stringClass, arrayOf("true"), 0).sub(stringClass)
|
||||||
|
VmType.ConstrainedType(intClass, arrayOf("true"), 0).sub(numberClass)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `alias - equality`() {
|
||||||
|
val mod =
|
||||||
|
makeModule(
|
||||||
|
"""
|
||||||
|
typealias A = Int
|
||||||
|
typealias B = A
|
||||||
|
typealias C = B
|
||||||
|
|
||||||
|
int: Int
|
||||||
|
a: A
|
||||||
|
b: B
|
||||||
|
c: C
|
||||||
|
"""
|
||||||
|
.trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
mod.getTypeForProperty("int").eq(mod.getTypeForProperty("a"))
|
||||||
|
mod.getTypeForProperty("int").eq(mod.getTypeForProperty("b"))
|
||||||
|
mod.getTypeForProperty("int").eq(mod.getTypeForProperty("c"))
|
||||||
|
mod.getTypeForProperty("a").eq(mod.getTypeForProperty("b"))
|
||||||
|
mod.getTypeForProperty("a").eq(mod.getTypeForProperty("c"))
|
||||||
|
intClass.eq(mod.getTypeForProperty("b"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `alias - subtype`() {
|
||||||
|
val mod =
|
||||||
|
makeModule(
|
||||||
|
"""
|
||||||
|
typealias A = Int
|
||||||
|
typealias B = A
|
||||||
|
typealias C = B
|
||||||
|
|
||||||
|
int: Int
|
||||||
|
a: A
|
||||||
|
b: B
|
||||||
|
c: C
|
||||||
|
"""
|
||||||
|
.trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
mod.getTypeForProperty("c").sub(numberClass)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `union - equality (ignores default index)`() {
|
||||||
|
VmType.UnionType(-1, arrayOf("foo", "bar", "baz"))
|
||||||
|
.eq(VmType.UnionType(0, arrayOf("foo", "bar", "baz")))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Disabled("TODO: support order-independent union type equivalence")
|
||||||
|
fun `union - equality, order-independent`() {
|
||||||
|
VmType.UnionType(-1, arrayOf("foo", "bar", "baz"))
|
||||||
|
.eq(VmType.UnionType(-1, arrayOf("bar", "baz", "foo")))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `union - subtype when all elements are subtype`() {
|
||||||
|
VmType.UnionType(-1, arrayOf("foo", "bar", "baz")).sub(stringClass)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `union - subtype of union when all elements are contained in supertype`() {
|
||||||
|
VmType.UnionType(-1, arrayOf("foo", "bar", "baz"))
|
||||||
|
.sub(VmType.UnionType(-1, arrayOf("foo", "bar", "baz", "qux")))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `type variable - treated like unknown, supertype and subtype to all`() {
|
||||||
|
VmType.TypeVariableType(TypeParameter(TypeParameter.Variance.INVARIANT, "Foo", 0))
|
||||||
|
.bidi(stringClass)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user