From 1b03410ad7236ebc209fe5cb7ae737bfb497c5f1 Mon Sep 17 00:00:00 2001 From: Islon Scherer Date: Wed, 24 Jun 2026 17:28:58 +0200 Subject: [PATCH 1/5] Improve error message for aliased references --- .../java/org/pkl/core/ast/type/TypeNode.java | 51 +++++++++++++------ .../pkl/core/ast/type/UnresolvedTypeNode.java | 20 +++++++- .../org/pkl/core/runtime/VmTypeAlias.java | 10 ++-- .../output/errors/reference10.err | 4 ++ .../output/errors/reference11.err | 4 ++ .../output/errors/reference8.err | 4 ++ .../output/errors/reference9.err | 4 ++ 7 files changed, 78 insertions(+), 19 deletions(-) diff --git a/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java b/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java index c823074bd..e86b25c54 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java @@ -2138,7 +2138,13 @@ public ReferenceTypeNode( this.domainTypeNode = domainTypeNode; this.referentTypeNode = referentTypeNode; this.getModuleNode = new GetModuleNode(sourceSection); - validateTypeArguments(sourceSection); + // A constraint written directly in this annotation's referent is reported against this + // annotation. Constraints introduced through a generic type alias's type argument are caught + // later. + if (findReferentConstraint() != null) { + CompilerDirectives.transferToInterpreter(); + throw exceptionBuilder().evalError("invalidReferenceTypeAnnotationWithConstraint").build(); + } } @Specialization @@ -2173,26 +2179,41 @@ private Object doEval(VmReference value, VmTyped module) { sourceSection, value, TypeNode.export(domainTypeNode), referentType); } - public void validateTypeArguments(@Nullable SourceSection aliasSourceSection) { - // constraints may not be used in Reference type annotation referents - // walk the type and throw if any part of the referent is constrained - - // TODO improve error message when this type node and/or referent constraint are behind type - // aliases + /** + * Type constraints may not appear in a {@code Reference}'s referent type argument. Walks the + * referent type and returns the source section of the first offending constraint, or {@code + * null} if the referent is constraint-free. + */ + public @Nullable SourceSection findReferentConstraint() { + var found = new SourceSection[1]; referentTypeNode.acceptTypeNode( true, (typeNode) -> { if (typeNode instanceof ConstrainedTypeNode) { - CompilerDirectives.transferToInterpreter(); - var err = - exceptionBuilder().evalError("invalidReferenceTypeAnnotationWithConstraint"); - if (aliasSourceSection != null) { - err.withSourceSection(aliasSourceSection); - } - throw err.build(); + found[0] = typeNode.getSourceSection(); + return false; } return true; }); + return found[0]; + } + + /** + * Signals that a {@code Reference} annotation reached through a generic type alias has a + * constrained referent. + */ + public static final class ReferentConstraintException extends RuntimeException { + private final SourceSection referenceTypeSection; + + public ReferentConstraintException(SourceSection referenceTypeSection) { + super(null, null, false, false); + this.referenceTypeSection = referenceTypeSection; + } + + /** The source section of the {@code Reference} annotation. */ + public SourceSection getReferenceTypeSection() { + return referenceTypeSection; + } } @Fallback @@ -2703,7 +2724,7 @@ public TypeAliasTypeNode( this.typeAlias = typeAlias; this.typeArgumentNodes = typeArgumentNodes; - aliasedTypeNode = typeAlias.instantiate(typeArgumentNodes, sourceSection); + aliasedTypeNode = typeAlias.instantiate(typeArgumentNodes); } public TypeNode getAliasedTypeNode() { diff --git a/pkl-core/src/main/java/org/pkl/core/ast/type/UnresolvedTypeNode.java b/pkl-core/src/main/java/org/pkl/core/ast/type/UnresolvedTypeNode.java index 1665cad49..300cbca5c 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/type/UnresolvedTypeNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/type/UnresolvedTypeNode.java @@ -306,7 +306,25 @@ public TypeNode execute(VirtualFrame frame) { for (var i = 0; i < argLength; i++) { resolvedTypeArgumentNodes[i] = typeArgumentNodes[i].execute(frame); } - return new TypeAliasTypeNode(sourceSection, typeAlias, resolvedTypeArgumentNodes); + try { + return new TypeAliasTypeNode(sourceSection, typeAlias, resolvedTypeArgumentNodes); + } catch (ReferenceTypeNode.ReferentConstraintException e) { + // a constraint reached a `Reference` referent through this generic alias. + var exception = + exceptionBuilder() + .evalError("invalidReferenceTypeAnnotationWithConstraint") + .withSourceSection(e.getReferenceTypeSection()) + .build(); + var rootNode = getRootNode(); + if (rootNode != null) { + exception + .getInsertedStackFrames() + .putIfAbsent( + rootNode.getCallTarget(), + VmUtils.createStackFrame(sourceSection, rootNode.getName())); + } + throw exception; + } } var module = (VmTyped) baseType; diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmTypeAlias.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmTypeAlias.java index 26bfb48d5..2280b86a4 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmTypeAlias.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmTypeAlias.java @@ -178,8 +178,7 @@ public Frame getEnclosingFrame() { } @TruffleBoundary - public TypeNode instantiate( - TypeNode[] typeArgumentNodes, SourceSection typeAliasTypeNodeSourceSection) { + public TypeNode instantiate(TypeNode[] typeArgumentNodes) { // Cloning the type node means that the entire type check remains within a single root node, // which should be good for interpreted and compiled performance alike: // * Fewer root nodes to call @@ -204,7 +203,12 @@ public TypeNode instantiate( clone.accept( node -> { if (node instanceof ReferenceTypeNode referenceTypeNode) { - referenceTypeNode.validateTypeArguments(typeAliasTypeNodeSourceSection); + // A type argument supplied at the alias usage site introduced a constraint into this + // `Reference`'s referent. + if (referenceTypeNode.findReferentConstraint() != null) { + throw new ReferenceTypeNode.ReferentConstraintException( + referenceTypeNode.getSourceSection()); + } } return true; }); diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference10.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference10.err index 461d5811a..29fa2be72 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference10.err +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference10.err @@ -1,6 +1,10 @@ –– Pkl Error –– `Reference` referent type argument may not include type constraints. +x | typealias Ref = ref.Reference + ^^^^^^^^^^^^^^^^^^^ +at reference10#test (file:///$snippetsDir/input/errors/reference10.pkl) + x | test = ref.Reference(d, String, "") as Ref> ^^^^^^^^^^^^^^^^^^^^^^^^^^ at reference10#test (file:///$snippetsDir/input/errors/reference10.pkl) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference11.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference11.err index ac3607b2a..d724b1bfa 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference11.err +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference11.err @@ -1,6 +1,10 @@ –– Pkl Error –– `Reference` referent type argument may not include type constraints. +x | typealias Ref = ref.Reference + ^^^^^^^^^^^^^^^^^^^ +at reference11#test (file:///$snippetsDir/input/errors/reference11.pkl) + x | test = ref.Reference(d, String, "") as Ref ^^^^^^^^^^^^ at reference11#test (file:///$snippetsDir/input/errors/reference11.pkl) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference8.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference8.err index 206779e8c..56115198f 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference8.err +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference8.err @@ -1,6 +1,10 @@ –– Pkl Error –– `Reference` referent type argument may not include type constraints. +x | typealias Ref = ref.Reference + ^^^^^^^^^^^^^^^^^^^ +at reference8#test (file:///$snippetsDir/input/errors/reference8.pkl) + x | test = ref.Reference(d, String, "") as Ref ^^^^^^^^^^^^^^^^^ at reference8#test (file:///$snippetsDir/input/errors/reference8.pkl) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference9.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference9.err index fa24b00ea..dba36c410 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference9.err +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference9.err @@ -1,6 +1,10 @@ –– Pkl Error –– `Reference` referent type argument may not include type constraints. +x | typealias Ref = ref.Reference + ^^^^^^^^^^^^^^^^^^^ +at reference9#test (file:///$snippetsDir/input/errors/reference9.pkl) + x | test = ref.Reference(d, String, "") as Ref ^^^^^^^^^^^^^^^^^^^^^^^ at reference9#test (file:///$snippetsDir/input/errors/reference9.pkl) From 5c1a1bbb48a4deb4f13c10f024afd8447d2d415b Mon Sep 17 00:00:00 2001 From: Islon Scherer Date: Thu, 25 Jun 2026 11:28:24 +0200 Subject: [PATCH 2/5] Show entire alias stack on error --- .../java/org/pkl/core/ast/type/TypeNode.java | 47 ++++++++++++++++--- .../pkl/core/ast/type/UnresolvedTypeNode.java | 14 +----- .../pkl/core/runtime/StackTraceGenerator.java | 4 ++ .../org/pkl/core/runtime/VmException.java | 13 +++++ .../org/pkl/core/runtime/VmTypeAlias.java | 9 ++-- .../input/errors/reference21.pkl | 7 +++ .../output/errors/reference10.err | 2 +- .../output/errors/reference11.err | 2 +- .../output/errors/reference21.err | 14 ++++++ .../output/errors/reference8.err | 2 +- .../output/errors/reference9.err | 2 +- 11 files changed, 88 insertions(+), 28 deletions(-) create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference21.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference21.err diff --git a/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java b/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java index e86b25c54..e15f259f8 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java @@ -28,6 +28,7 @@ import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.nodes.LoopNode; import com.oracle.truffle.api.source.SourceSection; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -38,6 +39,7 @@ import org.pkl.core.PType; import org.pkl.core.PType.StringLiteral; import org.pkl.core.PklBugException; +import org.pkl.core.StackFrame; import org.pkl.core.TypeParameter; import org.pkl.core.ast.*; import org.pkl.core.ast.builder.SymbolTable.CustomThisScope; @@ -2199,20 +2201,47 @@ private Object doEval(VmReference value, VmTyped module) { } /** - * Signals that a {@code Reference} annotation reached through a generic type alias has a + * Builds the chain of stack frames that lead a type constraint into this {@code Reference}'s + * referent through one or more generic type aliases. + */ + public List buildReferentConstraintStackFrames(VmTypeAlias outermostAlias) { + var aliasLayers = new ArrayList(); + for (var node = getParent(); node != null; node = node.getParent()) { + if (node instanceof TypeAliasTypeNode aliasNode) { + aliasLayers.add(aliasNode); + } + } + + var frames = new ArrayList(aliasLayers.size() + 1); + var annotationOwner = + aliasLayers.isEmpty() ? outermostAlias : aliasLayers.get(0).getTypeAlias(); + frames.add(VmUtils.createStackFrame(getSourceSection(), annotationOwner.getQualifiedName())); + + for (var i = 0; i < aliasLayers.size(); i++) { + var owner = + i + 1 < aliasLayers.size() ? aliasLayers.get(i + 1).getTypeAlias() : outermostAlias; + frames.add( + VmUtils.createStackFrame( + aliasLayers.get(i).getSourceSection(), owner.getQualifiedName())); + } + return frames; + } + + /** + * Signals that a {@code Reference} reached through one or more generic type aliases has a * constrained referent. */ public static final class ReferentConstraintException extends RuntimeException { - private final SourceSection referenceTypeSection; + private final List leadingStackFrames; - public ReferentConstraintException(SourceSection referenceTypeSection) { + public ReferentConstraintException(List leadingStackFrames) { super(null, null, false, false); - this.referenceTypeSection = referenceTypeSection; + this.leadingStackFrames = leadingStackFrames; } - /** The source section of the {@code Reference} annotation. */ - public SourceSection getReferenceTypeSection() { - return referenceTypeSection; + /** Frames for the {@code Reference} annotation and each enclosing type alias layer. */ + public List getLeadingStackFrames() { + return leadingStackFrames; } } @@ -2731,6 +2760,10 @@ public TypeNode getAliasedTypeNode() { return aliasedTypeNode; } + public VmTypeAlias getTypeAlias() { + return typeAlias; + } + @Override public FrameSlotKind getFrameSlotKind() { return aliasedTypeNode.getFrameSlotKind(); diff --git a/pkl-core/src/main/java/org/pkl/core/ast/type/UnresolvedTypeNode.java b/pkl-core/src/main/java/org/pkl/core/ast/type/UnresolvedTypeNode.java index 300cbca5c..a896fe8d2 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/type/UnresolvedTypeNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/type/UnresolvedTypeNode.java @@ -311,18 +311,8 @@ public TypeNode execute(VirtualFrame frame) { } catch (ReferenceTypeNode.ReferentConstraintException e) { // a constraint reached a `Reference` referent through this generic alias. var exception = - exceptionBuilder() - .evalError("invalidReferenceTypeAnnotationWithConstraint") - .withSourceSection(e.getReferenceTypeSection()) - .build(); - var rootNode = getRootNode(); - if (rootNode != null) { - exception - .getInsertedStackFrames() - .putIfAbsent( - rootNode.getCallTarget(), - VmUtils.createStackFrame(sourceSection, rootNode.getName())); - } + exceptionBuilder().evalError("invalidReferenceTypeAnnotationWithConstraint").build(); + exception.setLeadingStackFrames(e.getLeadingStackFrames()); throw exception; } } diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/StackTraceGenerator.java b/pkl-core/src/main/java/org/pkl/core/runtime/StackTraceGenerator.java index ee9fab8dc..6900111dd 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/StackTraceGenerator.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/StackTraceGenerator.java @@ -38,6 +38,10 @@ private StackTraceGenerator(VmException exception) { } private List capture() { + // frames that aren't part of the runtime call stack are + // shown ahead of the captured frames. + frames.addAll(exception.getLeadingStackFrames()); + var truffleElements = TruffleStackTrace.getStackTrace(exception); if (truffleElements.isEmpty()) { addFrame(exception.getSourceSection(), exception.getMemberName()); diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmException.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmException.java index ade09e98d..78493e8f7 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmException.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmException.java @@ -34,6 +34,7 @@ public abstract class VmException extends AbstractTruffleException { private final @Nullable SourceSection sourceSection; private final @Nullable String memberName; private final Map insertedStackFrames; + private List leadingStackFrames = List.of(); @Nullable private final BiConsumer messageBuilder; @Nullable protected BiConsumer hintBuilder; @@ -89,6 +90,18 @@ public final Map getInsertedStackFrames() { return insertedStackFrames; } + /** + * Stack frames to prepend to the rendered stack trace, ahead of the captured frames. Used to show + * source locations that aren't part of the runtime call stack. + */ + public final List getLeadingStackFrames() { + return leadingStackFrames; + } + + public final void setLeadingStackFrames(List leadingStackFrames) { + this.leadingStackFrames = leadingStackFrames; + } + public @Nullable BiConsumer getMessageBuilder() { return messageBuilder; } diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmTypeAlias.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmTypeAlias.java index 2280b86a4..790fa94cf 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmTypeAlias.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmTypeAlias.java @@ -202,13 +202,12 @@ public TypeNode instantiate(TypeNode[] typeArgumentNodes) { }); clone.accept( node -> { - if (node instanceof ReferenceTypeNode referenceTypeNode) { + if (node instanceof ReferenceTypeNode referenceTypeNode + && referenceTypeNode.findReferentConstraint() != null) { // A type argument supplied at the alias usage site introduced a constraint into this // `Reference`'s referent. - if (referenceTypeNode.findReferentConstraint() != null) { - throw new ReferenceTypeNode.ReferentConstraintException( - referenceTypeNode.getSourceSection()); - } + throw new ReferenceTypeNode.ReferentConstraintException( + referenceTypeNode.buildReferentConstraintStackFrames(this)); } return true; }); diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference21.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference21.pkl new file mode 100644 index 000000000..9c2699b47 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference21.pkl @@ -0,0 +1,7 @@ +import "pkl:ref" + +typealias Ref = ref.Reference + +typealias Ref2 = Ref + +foo: Ref2 diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference10.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference10.err index 29fa2be72..6511baf11 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference10.err +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference10.err @@ -3,7 +3,7 @@ x | typealias Ref = ref.Reference ^^^^^^^^^^^^^^^^^^^ -at reference10#test (file:///$snippetsDir/input/errors/reference10.pkl) +at reference10#Ref (file:///$snippetsDir/input/errors/reference10.pkl) x | test = ref.Reference(d, String, "") as Ref> ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference11.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference11.err index d724b1bfa..f1e1b6a66 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference11.err +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference11.err @@ -3,7 +3,7 @@ x | typealias Ref = ref.Reference ^^^^^^^^^^^^^^^^^^^ -at reference11#test (file:///$snippetsDir/input/errors/reference11.pkl) +at reference11#Ref (file:///$snippetsDir/input/errors/reference11.pkl) x | test = ref.Reference(d, String, "") as Ref ^^^^^^^^^^^^ diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference21.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference21.err new file mode 100644 index 000000000..681349cee --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference21.err @@ -0,0 +1,14 @@ +–– Pkl Error –– +`Reference` referent type argument may not include type constraints. + +x | typealias Ref = ref.Reference + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +at reference21#Ref (file:///$snippetsDir/input/errors/reference21.pkl) + +x | typealias Ref2 = Ref + ^^^^^^ +at reference21#Ref2 (file:///$snippetsDir/input/errors/reference21.pkl) + +x | foo: Ref2 + ^^^^^^^^^^^^^^^^^^^^^ +at reference21 (file:///$snippetsDir/input/errors/reference21.pkl) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference8.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference8.err index 56115198f..0202ab797 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference8.err +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference8.err @@ -3,7 +3,7 @@ x | typealias Ref = ref.Reference ^^^^^^^^^^^^^^^^^^^ -at reference8#test (file:///$snippetsDir/input/errors/reference8.pkl) +at reference8#Ref (file:///$snippetsDir/input/errors/reference8.pkl) x | test = ref.Reference(d, String, "") as Ref ^^^^^^^^^^^^^^^^^ diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference9.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference9.err index dba36c410..4e48cfb79 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference9.err +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference9.err @@ -3,7 +3,7 @@ x | typealias Ref = ref.Reference ^^^^^^^^^^^^^^^^^^^ -at reference9#test (file:///$snippetsDir/input/errors/reference9.pkl) +at reference9#Ref (file:///$snippetsDir/input/errors/reference9.pkl) x | test = ref.Reference(d, String, "") as Ref ^^^^^^^^^^^^^^^^^^^^^^^ From ee1e61ff9921c7b214ef77cbfd7d1f279246fea0 Mon Sep 17 00:00:00 2001 From: Islon Scherer Date: Fri, 26 Jun 2026 16:34:45 +0200 Subject: [PATCH 3/5] Fix review remarks --- .../java/org/pkl/core/ast/type/TypeNode.java | 130 +++++++++++------- .../pkl/core/ast/type/UnresolvedTypeNode.java | 10 +- .../org/pkl/core/runtime/VmTypeAlias.java | 12 -- .../input/errors/reference22.pkl | 6 + .../input/errors/reference23.pkl | 7 + .../output/errors/reference11.err | 8 ++ .../output/errors/reference12.err | 4 + .../output/errors/reference22.err | 14 ++ .../output/errors/reference23.err | 10 ++ 9 files changed, 131 insertions(+), 70 deletions(-) create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference22.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference23.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference22.err create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference23.err diff --git a/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java b/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java index e15f259f8..47169471a 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java @@ -27,6 +27,7 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.nodes.LoopNode; +import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.source.SourceSection; import java.util.ArrayList; import java.util.Arrays; @@ -56,6 +57,7 @@ import org.pkl.core.util.EconomicSets; import org.pkl.core.util.LateInit; import org.pkl.core.util.MutableBoolean; +import org.pkl.core.util.MutableReference; public abstract class TypeNode extends PklNode { @@ -2140,12 +2142,16 @@ public ReferenceTypeNode( this.domainTypeNode = domainTypeNode; this.referentTypeNode = referentTypeNode; this.getModuleNode = new GetModuleNode(sourceSection); - // A constraint written directly in this annotation's referent is reported against this - // annotation. Constraints introduced through a generic type alias's type argument are caught - // later. - if (findReferentConstraint() != null) { + // A type constraint anywhere in the referent is forbidden, including one reached through a + // type alias used in the referent. + var constraint = findReferentConstraint(); + if (constraint != null) { CompilerDirectives.transferToInterpreter(); - throw exceptionBuilder().evalError("invalidReferenceTypeAnnotationWithConstraint").build(); + var exception = + exceptionBuilder().evalError("invalidReferenceTypeAnnotationWithConstraint").build(); + exception.setLeadingStackFrames( + buildReferentConstraintFrames(constraint, getSourceSection(), null)); + throw exception; } } @@ -2182,67 +2188,66 @@ private Object doEval(VmReference value, VmTyped module) { } /** - * Type constraints may not appear in a {@code Reference}'s referent type argument. Walks the - * referent type and returns the source section of the first offending constraint, or {@code - * null} if the referent is constraint-free. + * Type constraints may not appear anywhere in a {@code Reference}'s referent type argument. + * Walks the referent type and returns the first offending {@link ConstrainedTypeNode} , or + * {@code null} if the referent is constraint-free. */ - public @Nullable SourceSection findReferentConstraint() { - var found = new SourceSection[1]; + public @Nullable ConstrainedTypeNode findReferentConstraint() { + var found = new MutableReference<@Nullable ConstrainedTypeNode>(null); referentTypeNode.acceptTypeNode( true, (typeNode) -> { - if (typeNode instanceof ConstrainedTypeNode) { - found[0] = typeNode.getSourceSection(); + if (typeNode instanceof ConstrainedTypeNode constrainedTypeNode) { + found.set(constrainedTypeNode); return false; } return true; }); - return found[0]; - } - - /** - * Builds the chain of stack frames that lead a type constraint into this {@code Reference}'s - * referent through one or more generic type aliases. - */ - public List buildReferentConstraintStackFrames(VmTypeAlias outermostAlias) { - var aliasLayers = new ArrayList(); - for (var node = getParent(); node != null; node = node.getParent()) { - if (node instanceof TypeAliasTypeNode aliasNode) { - aliasLayers.add(aliasNode); + return found.getOrNull(); + } + + /** Builds the frames to show ahead of an "invalid referent constraint" error. */ + public static List buildReferentConstraintFrames( + ConstrainedTypeNode constraintNode, + SourceSection usageSection, + @Nullable VmTypeAlias outermostAlias) { + var frames = new ArrayList(); + for (Node node = constraintNode; node != null; node = node.getParent()) { + if (!(node instanceof ConstrainedTypeNode + || node instanceof TypeAliasTypeNode + || node instanceof ReferenceTypeNode)) { + continue; + } + var section = node.getSourceSection(); + if (section == null || !section.isAvailable() || isWithin(usageSection, section)) { + continue; + } + var owner = ownerAlias(node, outermostAlias); + if (owner != null) { + frames.add(VmUtils.createStackFrame(section, owner.getQualifiedName())); } - } - - var frames = new ArrayList(aliasLayers.size() + 1); - var annotationOwner = - aliasLayers.isEmpty() ? outermostAlias : aliasLayers.get(0).getTypeAlias(); - frames.add(VmUtils.createStackFrame(getSourceSection(), annotationOwner.getQualifiedName())); - - for (var i = 0; i < aliasLayers.size(); i++) { - var owner = - i + 1 < aliasLayers.size() ? aliasLayers.get(i + 1).getTypeAlias() : outermostAlias; - frames.add( - VmUtils.createStackFrame( - aliasLayers.get(i).getSourceSection(), owner.getQualifiedName())); } return frames; } /** - * Signals that a {@code Reference} reached through one or more generic type aliases has a - * constrained referent. + * The type alias whose body contains {@code node}: the nearest enclosing alias, else the + * outermost alias being instantiated (which is {@code null} for a directly-used Reference). */ - public static final class ReferentConstraintException extends RuntimeException { - private final List leadingStackFrames; - - public ReferentConstraintException(List leadingStackFrames) { - super(null, null, false, false); - this.leadingStackFrames = leadingStackFrames; + private static @Nullable VmTypeAlias ownerAlias( + Node node, @Nullable VmTypeAlias outermostAlias) { + for (var parent = node.getParent(); parent != null; parent = parent.getParent()) { + if (parent instanceof TypeAliasTypeNode aliasNode) { + return aliasNode.getTypeAlias(); + } } + return outermostAlias; + } - /** Frames for the {@code Reference} annotation and each enclosing type alias layer. */ - public List getLeadingStackFrames() { - return leadingStackFrames; - } + private static boolean isWithin(SourceSection outer, SourceSection inner) { + return inner.getSource().equals(outer.getSource()) + && inner.getCharIndex() >= outer.getCharIndex() + && inner.getCharEndIndex() <= outer.getCharEndIndex(); } @Fallback @@ -2754,6 +2759,33 @@ public TypeAliasTypeNode( this.typeAlias = typeAlias; this.typeArgumentNodes = typeArgumentNodes; aliasedTypeNode = typeAlias.instantiate(typeArgumentNodes); + checkReferentConstraints(typeAlias); + } + + /** + * Reports a forbidden type constraint that a type argument introduced into a {@code + * Reference}'s referent through this (generic) alias. The error is reported at this usage type + * expression, with leading frames for the constraint and every alias layer it passed through. + */ + private void checkReferentConstraints(VmTypeAlias outermostAlias) { + aliasedTypeNode.accept( + node -> { + if (node instanceof ReferenceTypeNode referenceTypeNode) { + var constraint = referenceTypeNode.findReferentConstraint(); + if (constraint != null) { + CompilerDirectives.transferToInterpreter(); + var exception = + exceptionBuilder() + .evalError("invalidReferenceTypeAnnotationWithConstraint") + .build(); + exception.setLeadingStackFrames( + ReferenceTypeNode.buildReferentConstraintFrames( + constraint, getSourceSection(), outermostAlias)); + throw exception; + } + } + return true; + }); } public TypeNode getAliasedTypeNode() { diff --git a/pkl-core/src/main/java/org/pkl/core/ast/type/UnresolvedTypeNode.java b/pkl-core/src/main/java/org/pkl/core/ast/type/UnresolvedTypeNode.java index a896fe8d2..1665cad49 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/type/UnresolvedTypeNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/type/UnresolvedTypeNode.java @@ -306,15 +306,7 @@ public TypeNode execute(VirtualFrame frame) { for (var i = 0; i < argLength; i++) { resolvedTypeArgumentNodes[i] = typeArgumentNodes[i].execute(frame); } - try { - return new TypeAliasTypeNode(sourceSection, typeAlias, resolvedTypeArgumentNodes); - } catch (ReferenceTypeNode.ReferentConstraintException e) { - // a constraint reached a `Reference` referent through this generic alias. - var exception = - exceptionBuilder().evalError("invalidReferenceTypeAnnotationWithConstraint").build(); - exception.setLeadingStackFrames(e.getLeadingStackFrames()); - throw exception; - } + return new TypeAliasTypeNode(sourceSection, typeAlias, resolvedTypeArgumentNodes); } var module = (VmTyped) baseType; diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmTypeAlias.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmTypeAlias.java index 790fa94cf..03480827a 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmTypeAlias.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmTypeAlias.java @@ -32,7 +32,6 @@ import org.pkl.core.ast.VmModifier; import org.pkl.core.ast.type.TypeNode; import org.pkl.core.ast.type.TypeNode.ConstrainedTypeNode; -import org.pkl.core.ast.type.TypeNode.ReferenceTypeNode; import org.pkl.core.ast.type.TypeNode.TypeVariableNode; import org.pkl.core.ast.type.TypeNode.UnknownTypeNode; import org.pkl.core.util.LateInit; @@ -200,17 +199,6 @@ public TypeNode instantiate(TypeNode[] typeArgumentNodes) { } return true; }); - clone.accept( - node -> { - if (node instanceof ReferenceTypeNode referenceTypeNode - && referenceTypeNode.findReferentConstraint() != null) { - // A type argument supplied at the alias usage site introduced a constraint into this - // `Reference`'s referent. - throw new ReferenceTypeNode.ReferentConstraintException( - referenceTypeNode.buildReferentConstraintStackFrames(this)); - } - return true; - }); return clone; } diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference22.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference22.pkl new file mode 100644 index 000000000..ff2ebb1a2 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference22.pkl @@ -0,0 +1,6 @@ +import "pkl:ref" + +typealias Ref = ref.Reference +typealias Ref2 = Ref + +foo: Ref2 diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference23.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference23.pkl new file mode 100644 index 000000000..d57c86120 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference23.pkl @@ -0,0 +1,7 @@ + +import "pkl:ref" + +typealias Foo1 = Foo2 +typealias Foo2 = String(isEmpty) + +foo: ref.Reference diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference11.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference11.err index f1e1b6a66..819e5efaf 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference11.err +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference11.err @@ -1,6 +1,14 @@ –– Pkl Error –– `Reference` referent type argument may not include type constraints. +xx | typealias Alias2 = String(length < 5) + ^^^^^^^^^^^^^^^^^^ +at reference11#Alias2 (file:///$snippetsDir/input/errors/reference11.pkl) + +xx | typealias Alias1 = Int | Alias2? + ^^^^^^ +at reference11#Alias1 (file:///$snippetsDir/input/errors/reference11.pkl) + x | typealias Ref = ref.Reference ^^^^^^^^^^^^^^^^^^^ at reference11#Ref (file:///$snippetsDir/input/errors/reference11.pkl) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference12.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference12.err index cde60de77..b14383b5f 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference12.err +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference12.err @@ -1,6 +1,10 @@ –– Pkl Error –– `Reference` referent type argument may not include type constraints. +xx | typealias Alias2 = String(length < 5) + ^^^^^^^^^^^^^^^^^^ +at reference12#Alias2 (file:///$snippetsDir/input/errors/reference12.pkl) + x | typealias RefAlias1 = ref.Reference ^^^^^^^^^^^^^^^^^^^^^^^^^ at reference12#RefAlias1 (file:///$snippetsDir/input/errors/reference12.pkl) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference22.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference22.err new file mode 100644 index 000000000..fe0507e48 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference22.err @@ -0,0 +1,14 @@ +–– Pkl Error –– +`Reference` referent type argument may not include type constraints. + +x | typealias Ref = ref.Reference + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +at reference22#Ref (file:///$snippetsDir/input/errors/reference22.pkl) + +x | typealias Ref2 = Ref + ^^^^^^^^^^^^^^^ +at reference22#Ref2 (file:///$snippetsDir/input/errors/reference22.pkl) + +x | foo: Ref2 + ^^^^ +at reference22 (file:///$snippetsDir/input/errors/reference22.pkl) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference23.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference23.err new file mode 100644 index 000000000..2ea4f4c17 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference23.err @@ -0,0 +1,10 @@ +–– Pkl Error –– +`Reference` referent type argument may not include type constraints. + +x | typealias Foo2 = String(isEmpty) + ^^^^^^^^^^^^^^^ +at reference23#Foo2 (file:///$snippetsDir/input/errors/reference23.pkl) + +x | foo: ref.Reference + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +at reference23 (file:///$snippetsDir/input/errors/reference23.pkl) From 4d79c1c8fc84fa565537de15c2e830e41636ec6f Mon Sep 17 00:00:00 2001 From: Islon Scherer Date: Fri, 26 Jun 2026 16:55:21 +0200 Subject: [PATCH 4/5] Move leading stack frames to VmExceptionBuilder --- .../java/org/pkl/core/ast/type/TypeNode.java | 24 ++-- .../pkl/core/runtime/VmExceptionBuilder.java | 129 ++++++++++-------- 2 files changed, 82 insertions(+), 71 deletions(-) diff --git a/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java b/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java index 47169471a..657528843 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java @@ -2147,11 +2147,11 @@ public ReferenceTypeNode( var constraint = findReferentConstraint(); if (constraint != null) { CompilerDirectives.transferToInterpreter(); - var exception = - exceptionBuilder().evalError("invalidReferenceTypeAnnotationWithConstraint").build(); - exception.setLeadingStackFrames( - buildReferentConstraintFrames(constraint, getSourceSection(), null)); - throw exception; + throw exceptionBuilder() + .evalError("invalidReferenceTypeAnnotationWithConstraint") + .withLeadingStackFrames( + buildReferentConstraintFrames(constraint, getSourceSection(), null)) + .build(); } } @@ -2774,14 +2774,12 @@ private void checkReferentConstraints(VmTypeAlias outermostAlias) { var constraint = referenceTypeNode.findReferentConstraint(); if (constraint != null) { CompilerDirectives.transferToInterpreter(); - var exception = - exceptionBuilder() - .evalError("invalidReferenceTypeAnnotationWithConstraint") - .build(); - exception.setLeadingStackFrames( - ReferenceTypeNode.buildReferentConstraintFrames( - constraint, getSourceSection(), outermostAlias)); - throw exception; + throw exceptionBuilder() + .evalError("invalidReferenceTypeAnnotationWithConstraint") + .withLeadingStackFrames( + ReferenceTypeNode.buildReferentConstraintFrames( + constraint, getSourceSection(), outermostAlias)) + .build(); } } return true; diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmExceptionBuilder.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmExceptionBuilder.java index 267db2570..ae998b401 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmExceptionBuilder.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmExceptionBuilder.java @@ -99,6 +99,7 @@ public String toString() { private @Nullable Node location; private @Nullable SourceSection sourceSection; private @Nullable String memberName; + private List leadingStackFrames = List.of(); public VmExceptionBuilder typeMismatch(Object value, VmClass expectedType) { if (value instanceof VmNull) { @@ -359,6 +360,15 @@ public VmExceptionBuilder withInsertedStackFrames( return this; } + /** + * Frames to show ahead of the captured stack trace (see {@link + * VmException#getLeadingStackFrames()}). + */ + public VmExceptionBuilder withLeadingStackFrames(List leadingStackFrames) { + this.leadingStackFrames = leadingStackFrames; + return this; + } + public VmException build() { if (message != null && messageBuilder != null) { throw new IllegalStateException("Both message and messageBuilder are set"); @@ -374,64 +384,67 @@ public VmException build() { var effectiveInsertedStackFrames = insertedStackFrames == null ? new HashMap() : insertedStackFrames; - return switch (kind) { - case EVAL_ERROR -> - new VmEvalException( - message, - cause, - isExternalMessage, - messageArguments, - messageBuilder, - programValues, - location, - sourceSection, - memberName, - hintBuilder, - effectiveInsertedStackFrames); - case UNDEFINED_VALUE -> - new VmUndefinedValueException( - message, - cause, - isExternalMessage, - messageArguments, - messageBuilder, - programValues, - location, - sourceSection, - memberName, - hintBuilder, - receiver, - effectiveInsertedStackFrames); - case BUG -> - new VmBugException( - message, - cause, - isExternalMessage, - messageArguments, - messageBuilder, - programValues, - location, - sourceSection, - memberName, - hintBuilder, - effectiveInsertedStackFrames); - case WRAPPED -> { - assert wrappedException != null; - yield new VmWrappedEvalException( - message, - cause, - isExternalMessage, - messageArguments, - messageBuilder, - programValues, - location, - sourceSection, - memberName, - hintBuilder, - effectiveInsertedStackFrames, - wrappedException); - } - }; + var exception = + switch (kind) { + case EVAL_ERROR -> + new VmEvalException( + message, + cause, + isExternalMessage, + messageArguments, + messageBuilder, + programValues, + location, + sourceSection, + memberName, + hintBuilder, + effectiveInsertedStackFrames); + case UNDEFINED_VALUE -> + new VmUndefinedValueException( + message, + cause, + isExternalMessage, + messageArguments, + messageBuilder, + programValues, + location, + sourceSection, + memberName, + hintBuilder, + receiver, + effectiveInsertedStackFrames); + case BUG -> + new VmBugException( + message, + cause, + isExternalMessage, + messageArguments, + messageBuilder, + programValues, + location, + sourceSection, + memberName, + hintBuilder, + effectiveInsertedStackFrames); + case WRAPPED -> { + assert wrappedException != null; + yield new VmWrappedEvalException( + message, + cause, + isExternalMessage, + messageArguments, + messageBuilder, + programValues, + location, + sourceSection, + memberName, + hintBuilder, + effectiveInsertedStackFrames, + wrappedException); + } + }; + exception.setLeadingStackFrames(leadingStackFrames); + return exception; } private List collectPropertyNames(VmObjectLike object, boolean isRead) { From b1ab1b7253e2bf3fcfd66ed416b4e76bec18af89 Mon Sep 17 00:00:00 2001 From: Islon Scherer Date: Mon, 29 Jun 2026 13:56:26 +0200 Subject: [PATCH 5/5] Fix review remarks --- .../java/org/pkl/core/ast/type/TypeNode.java | 11 +- .../org/pkl/core/runtime/VmBugException.java | 6 +- .../org/pkl/core/runtime/VmEvalException.java | 6 +- .../org/pkl/core/runtime/VmException.java | 10 +- .../pkl/core/runtime/VmExceptionBuilder.java | 123 +++++++++--------- .../runtime/VmStackOverflowException.java | 3 +- .../runtime/VmUndefinedValueException.java | 6 +- .../core/runtime/VmWrappedEvalException.java | 4 +- 8 files changed, 90 insertions(+), 79 deletions(-) diff --git a/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java b/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java index 657528843..455f5bf68 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java @@ -28,6 +28,7 @@ import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.nodes.LoopNode; import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.NodeUtil; import com.oracle.truffle.api.source.SourceSection; import java.util.ArrayList; import java.util.Arrays; @@ -2219,6 +2220,7 @@ public static List buildReferentConstraintFrames( continue; } var section = node.getSourceSection(); + //noinspection ConstantValue if (section == null || !section.isAvailable() || isWithin(usageSection, section)) { continue; } @@ -2234,12 +2236,13 @@ public static List buildReferentConstraintFrames( * The type alias whose body contains {@code node}: the nearest enclosing alias, else the * outermost alias being instantiated (which is {@code null} for a directly-used Reference). */ + @SuppressWarnings("DataFlowIssue") private static @Nullable VmTypeAlias ownerAlias( Node node, @Nullable VmTypeAlias outermostAlias) { - for (var parent = node.getParent(); parent != null; parent = parent.getParent()) { - if (parent instanceof TypeAliasTypeNode aliasNode) { - return aliasNode.getTypeAlias(); - } + var parent = NodeUtil.findParent(node, TypeAliasTypeNode.class); + //noinspection ConstantValue + if (parent != null) { + return parent.typeAlias; } return outermostAlias; } diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmBugException.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmBugException.java index 4c839477b..2298652fd 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmBugException.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmBugException.java @@ -38,7 +38,8 @@ public VmBugException( @Nullable SourceSection sourceSection, @Nullable String memberName, @Nullable BiConsumer hintBuilder, - Map insertedStackFrames) { + Map insertedStackFrames, + List leadingStackFrames) { super( message, @@ -51,7 +52,8 @@ public VmBugException( sourceSection, memberName, hintBuilder, - insertedStackFrames); + insertedStackFrames, + leadingStackFrames); } @Override diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmEvalException.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmEvalException.java index 24a20d01a..0befe6735 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmEvalException.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmEvalException.java @@ -37,7 +37,8 @@ public VmEvalException( @Nullable SourceSection sourceSection, @Nullable String memberName, @Nullable BiConsumer hintBuilder, - Map insertedStackFrames) { + Map insertedStackFrames, + List leadingStackFrames) { super( message, @@ -50,6 +51,7 @@ public VmEvalException( sourceSection, memberName, hintBuilder, - insertedStackFrames); + insertedStackFrames, + leadingStackFrames); } } diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmException.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmException.java index 78493e8f7..d518bd327 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmException.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmException.java @@ -34,7 +34,7 @@ public abstract class VmException extends AbstractTruffleException { private final @Nullable SourceSection sourceSection; private final @Nullable String memberName; private final Map insertedStackFrames; - private List leadingStackFrames = List.of(); + private final List leadingStackFrames; @Nullable private final BiConsumer messageBuilder; @Nullable protected BiConsumer hintBuilder; @@ -49,7 +49,8 @@ public VmException( @Nullable SourceSection sourceSection, @Nullable String memberName, @Nullable BiConsumer hintBuilder, - Map insertedStackFrames) { + Map insertedStackFrames, + List leadingStackFrames) { super(message, cause, UNLIMITED_STACK_TRACE, location); assert message != null || messageBuilder != null; this.messageBuilder = messageBuilder; @@ -59,6 +60,7 @@ public VmException( this.sourceSection = sourceSection; this.memberName = memberName; this.insertedStackFrames = insertedStackFrames; + this.leadingStackFrames = leadingStackFrames; this.hintBuilder = hintBuilder; } @@ -98,10 +100,6 @@ public final List getLeadingStackFrames() { return leadingStackFrames; } - public final void setLeadingStackFrames(List leadingStackFrames) { - this.leadingStackFrames = leadingStackFrames; - } - public @Nullable BiConsumer getMessageBuilder() { return messageBuilder; } diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmExceptionBuilder.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmExceptionBuilder.java index ae998b401..cd0fc298b 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmExceptionBuilder.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmExceptionBuilder.java @@ -384,67 +384,68 @@ public VmException build() { var effectiveInsertedStackFrames = insertedStackFrames == null ? new HashMap() : insertedStackFrames; - var exception = - switch (kind) { - case EVAL_ERROR -> - new VmEvalException( - message, - cause, - isExternalMessage, - messageArguments, - messageBuilder, - programValues, - location, - sourceSection, - memberName, - hintBuilder, - effectiveInsertedStackFrames); - case UNDEFINED_VALUE -> - new VmUndefinedValueException( - message, - cause, - isExternalMessage, - messageArguments, - messageBuilder, - programValues, - location, - sourceSection, - memberName, - hintBuilder, - receiver, - effectiveInsertedStackFrames); - case BUG -> - new VmBugException( - message, - cause, - isExternalMessage, - messageArguments, - messageBuilder, - programValues, - location, - sourceSection, - memberName, - hintBuilder, - effectiveInsertedStackFrames); - case WRAPPED -> { - assert wrappedException != null; - yield new VmWrappedEvalException( - message, - cause, - isExternalMessage, - messageArguments, - messageBuilder, - programValues, - location, - sourceSection, - memberName, - hintBuilder, - effectiveInsertedStackFrames, - wrappedException); - } - }; - exception.setLeadingStackFrames(leadingStackFrames); - return exception; + return switch (kind) { + case EVAL_ERROR -> + new VmEvalException( + message, + cause, + isExternalMessage, + messageArguments, + messageBuilder, + programValues, + location, + sourceSection, + memberName, + hintBuilder, + effectiveInsertedStackFrames, + leadingStackFrames); + case UNDEFINED_VALUE -> + new VmUndefinedValueException( + message, + cause, + isExternalMessage, + messageArguments, + messageBuilder, + programValues, + location, + sourceSection, + memberName, + hintBuilder, + receiver, + effectiveInsertedStackFrames, + leadingStackFrames); + case BUG -> + new VmBugException( + message, + cause, + isExternalMessage, + messageArguments, + messageBuilder, + programValues, + location, + sourceSection, + memberName, + hintBuilder, + effectiveInsertedStackFrames, + leadingStackFrames); + case WRAPPED -> { + assert wrappedException != null; + yield new VmWrappedEvalException( + message, + cause, + isExternalMessage, + messageArguments, + messageBuilder, + programValues, + location, + sourceSection, + memberName, + hintBuilder, + effectiveInsertedStackFrames, + leadingStackFrames, + wrappedException); + } + }; } private List collectPropertyNames(VmObjectLike object, boolean isRead) { diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmStackOverflowException.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmStackOverflowException.java index 29d8859f7..ed9c203cb 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmStackOverflowException.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmStackOverflowException.java @@ -32,6 +32,7 @@ public VmStackOverflowException(StackOverflowError e) { null, null, null, - new HashMap<>()); + new HashMap<>(), + List.of()); } } diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmUndefinedValueException.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmUndefinedValueException.java index 4a6577750..562fcc6b2 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmUndefinedValueException.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmUndefinedValueException.java @@ -43,7 +43,8 @@ public VmUndefinedValueException( @Nullable String memberName, @Nullable BiConsumer hintBuilder, @Nullable Object receiver, - @Nullable Map insertedStackFrames) { + @Nullable Map insertedStackFrames, + List leadingStackFrames) { super( message, @@ -56,7 +57,8 @@ public VmUndefinedValueException( sourceSection, memberName, hintBuilder, - insertedStackFrames == null ? Collections.emptyMap() : insertedStackFrames); + insertedStackFrames == null ? Collections.emptyMap() : insertedStackFrames, + leadingStackFrames); this.receiver = receiver; } diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmWrappedEvalException.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmWrappedEvalException.java index 88ada6fac..16db32dcb 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmWrappedEvalException.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmWrappedEvalException.java @@ -41,6 +41,7 @@ public VmWrappedEvalException( @Nullable String memberName, @Nullable BiConsumer hintBuilder, Map insertedStackFrames, + List leadingStackFrames, VmException wrappedException) { super( message, @@ -53,7 +54,8 @@ public VmWrappedEvalException( sourceSection, memberName, hintBuilder, - insertedStackFrames); + insertedStackFrames, + leadingStackFrames); this.wrappedException = wrappedException; }