Improve error message thrown for "cannot instantiate" (#1916)

If the parent comes from an implicit node (e.g. read super entry node),
Pkl will currently bury its source section when rendering the error
message.
This commit is contained in:
Daniel Chao authored and GitHub committed 2026-10-20 08:42:18 -07:00
1 parent ce977f1ffe
commit a06684b2c0
9 files changed
+48 -13

No files matched your search

@@ -98,7 +98,7 @@ public abstract class GeneratorObjectLiteralNode extends ObjectLiteralNode {
@Specialization(guards = "checkObjectCannotHaveParameters()")
protected VmTyped evalTyped(VirtualFrame frame, VmTyped parent) {
VmUtils.checkIsInstantiable(parent.getVmClass(), getParentNode());
VmUtils.checkIsInstantiable(parent.getVmClass(), getParentNode(), this);
var data = executeChildren(frame, parent, 0);
if (data.hasNoMembers()) {
return parent;
@@ -207,7 +207,7 @@ public abstract class GeneratorObjectLiteralNode extends ObjectLiteralNode {
@Specialization(guards = {"isTypedObjectClass(parent)", "checkObjectCannotHaveParameters()"})
protected VmTyped evalTypedObjectClass(VirtualFrame frame, VmClass parent) {
VmUtils.checkIsInstantiable(parent, getParentNode());
VmUtils.checkIsInstantiable(parent, getParentNode(), this);
var data = executeChildren(frame, parent, 0);
if (data.hasNoMembers()) {
return parent.getPrototype();
@@ -225,7 +225,9 @@ public abstract class GeneratorObjectLiteralNode extends ObjectLiteralNode {
value = getNullDefaultValue(vmNull);
}
VmUtils.checkIsInstantiable(
value instanceof VmClass vmClass ? vmClass : VmUtils.getClass(value), getParentNode());
value instanceof VmClass vmClass ? vmClass : VmUtils.getClass(value),
getParentNode(),
this);
throw exceptionBuilder().unreachableCode().build();
}
@@ -52,7 +52,7 @@ public abstract class EmptyObjectLiteralNode extends ExpressionNode {
return VmDynamic.empty();
}
VmUtils.checkIsInstantiable(parent, getParentNode());
VmUtils.checkIsInstantiable(parent, getParentNode(), this);
return parent.getPrototype();
}
@@ -70,7 +70,7 @@ public abstract class EmptyObjectLiteralNode extends ExpressionNode {
@TruffleBoundary
protected void fallback(Object parent) {
assert !(parent instanceof VmClass);
VmUtils.checkIsInstantiable(VmUtils.getClass(parent), getParentNode());
VmUtils.checkIsInstantiable(VmUtils.getClass(parent), getParentNode(), this);
throw exceptionBuilder().unreachableCode().build();
}
@@ -72,7 +72,7 @@ public abstract class SpecializedObjectLiteralNode extends ObjectLiteralNode {
@Idempotent
protected boolean checkIsValidTypedAmendment(Object parent) {
var parentClass = parent instanceof VmClass vmClass ? vmClass : VmUtils.getClass(parent);
VmUtils.checkIsInstantiable(parentClass, getParentNode());
VmUtils.checkIsInstantiable(parentClass, getParentNode(), this);
for (var member : EconomicMaps.getValues(members)) {
if (member.isLocal()) continue;
@@ -290,7 +290,7 @@ public abstract class SpecializedObjectLiteralNode extends ObjectLiteralNode {
Object parent, @Nullable ObjectMember firstElemOrEntry, boolean isElementsOnly) {
var parentIsClass = parent instanceof VmClass;
var parentClass = parentIsClass ? (VmClass) parent : VmUtils.getClass(parent);
VmUtils.checkIsInstantiable(parentClass, getParentNode());
VmUtils.checkIsInstantiable(parentClass, getParentNode(), this);
var property = findFirstNonDefaultProperty(members);
if (property != null
@@ -59,7 +59,7 @@ public abstract class AbstractInferParentNode extends ExpressionNode {
// try to produce a more specific error message than "cannotInstantiateType"
var clazz = typeNode.getType().getVmClass();
if (clazz != null) {
VmUtils.checkIsInstantiable(clazz, typeNode);
VmUtils.checkIsInstantiable(clazz, typeNode, this);
}
throw exceptionBuilder()
@@ -903,7 +903,7 @@ public final class VmUtils {
return resolvedNodes;
}
public static void checkIsInstantiable(VmClass parentClass, @Nullable Node parentNode) {
public static void checkIsInstantiable(VmClass parentClass, Node parentNode, Node callerNode) {
if (parentClass.isInstantiable()) return;
CompilerDirectives.transferToInterpreter();
@@ -911,14 +911,15 @@ public final class VmUtils {
if (parentClass.isAbstract()) {
throw new VmExceptionBuilder()
.evalError("cannotInstantiateAbstractClass", parentClass)
.withOptionalLocation(parentNode)
.withOptionalLocation(
parentNode.getSourceSection().isAvailable() ? parentNode : callerNode)
.build();
}
assert parentClass.isExternal();
throw new VmExceptionBuilder()
.evalError("cannotInstantiateExternalClass", parentClass)
.withOptionalLocation(parentNode)
.withOptionalLocation(parentNode.getSourceSection().isAvailable() ? parentNode : callerNode)
.build();
}
@@ -108,7 +108,7 @@ public final class DynamicNodes {
throw exceptionBuilder().evalError("notASubclassOfTyped", clazz).build();
}
VmUtils.checkIsInstantiable(clazz, this);
VmUtils.checkIsInstantiable(clazz, this, this);
var result =
new VmTyped(
@@ -255,7 +255,7 @@ public final class MapNodes {
throw exceptionBuilder().evalError("notASubclassOfTyped", clazz).build();
}
VmUtils.checkIsInstantiable(clazz, null);
VmUtils.checkIsInstantiable(clazz, this, this);
var result =
new VmTyped(
@@ -0,0 +1,13 @@
class Holder {
name: String
}
base: Holder = new {
name = "base"
}
result: Holder = (base) {
name {
"foo"
}
}
@@ -0,0 +1,19 @@
–– Pkl Error ––
Cannot instantiate, or amend an instance of, external class `String`.
xx | name {
^^^^^^
at cannotInstantiateExternalClass#result.name (file:///$snippetsDir/input/errors/cannotInstantiateExternalClass.pkl)
To create an instance of an external class, use its literal syntax or constructor method.
Examples:
* `123` uses literal syntax to create an instance of class `Int`.
* `Pair(1, 2)` uses the `Pair()` constructor method to create an instance of class `Pair`.
xxx | renderer.renderDocument(value)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at pkl.base#Module.output.text (pkl:base)
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
^^^^
at pkl.base#Module.output.bytes (pkl:base)