From f61a5cf5417e726c71b8fec94104835ffbf9e027 Mon Sep 17 00:00:00 2001 From: Daniel Chao Date: Wed, 24 Jun 2026 02:48:57 -0700 Subject: [PATCH] Check if receiver is a module object in `relativePathTo` (#1650) Closes #1649 --- .../pkl/core/stdlib/base/ModuleClassNodes.java | 18 ++++++++++-------- .../org/pkl/core/errorMessages.properties | 5 ++++- .../input/errors/relativePathToNotAModule1.pkl | 7 +++++++ .../input/errors/relativePathToNotAModule2.pkl | 8 ++++++++ .../errors/relativePathToNotAModule1.err | 14 ++++++++++++++ .../errors/relativePathToNotAModule2.err | 14 ++++++++++++++ 6 files changed, 57 insertions(+), 9 deletions(-) create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input/errors/relativePathToNotAModule1.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input/errors/relativePathToNotAModule2.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/errors/relativePathToNotAModule1.err create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/errors/relativePathToNotAModule2.err diff --git a/pkl-core/src/main/java/org/pkl/core/stdlib/base/ModuleClassNodes.java b/pkl-core/src/main/java/org/pkl/core/stdlib/base/ModuleClassNodes.java index e7be339f4..d4998bda5 100644 --- a/pkl-core/src/main/java/org/pkl/core/stdlib/base/ModuleClassNodes.java +++ b/pkl-core/src/main/java/org/pkl/core/stdlib/base/ModuleClassNodes.java @@ -1,5 +1,5 @@ /* - * Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-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. @@ -15,6 +15,7 @@ */ package org.pkl.core.stdlib.base; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Specialization; import java.net.URI; @@ -30,15 +31,16 @@ public final class ModuleClassNodes { @Specialization @TruffleBoundary protected VmList eval(VmObjectLike self, VmObjectLike other) { + if (!self.isModuleObject()) { + CompilerDirectives.transferToInterpreter(); + throw exceptionBuilder().evalError("expectedModuleAsReceiver").build(); + } + if (!other.isModuleObject()) { + CompilerDirectives.transferToInterpreter(); + throw exceptionBuilder().evalError("expectedModuleAsArgument").build(); + } var selfKey = VmUtils.getModuleInfo(self).getModuleKey(); var selfUri = selfKey.getUri(); - - if (!other.isModuleObject()) { - throw exceptionBuilder() - .evalError("expectedModule") - // No meaningful SourceSection available, in this case. - .build(); - } var otherKey = VmUtils.getModuleInfo(other).getModuleKey(); var otherUri = otherKey.getUri(); diff --git a/pkl-core/src/main/resources/org/pkl/core/errorMessages.properties b/pkl-core/src/main/resources/org/pkl/core/errorMessages.properties index e3b93b6b7..f72d2946a 100644 --- a/pkl-core/src/main/resources/org/pkl/core/errorMessages.properties +++ b/pkl-core/src/main/resources/org/pkl/core/errorMessages.properties @@ -203,7 +203,10 @@ Class `{0}` does not have type parameters. notASubclassOfTyped=\ Class `{0}` is not a subtype of `Typed`. -expectedModule=\ +expectedModuleAsReceiver=\ +Expected a module as the receiver, but got an object that amends a module. + +expectedModuleAsArgument=\ Expected a module as argument, but got an object that amends a module. wrongTypeArgumentCount=\ diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/errors/relativePathToNotAModule1.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/relativePathToNotAModule1.pkl new file mode 100644 index 000000000..5a442d409 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/relativePathToNotAModule1.pkl @@ -0,0 +1,7 @@ +local objectThatAmendsModule = (module) { + // add a local property to force this into a new object amends; + // empty object amends are optimized away. + local qux = 1 +} + +res = objectThatAmendsModule.relativePathTo(module) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/errors/relativePathToNotAModule2.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/relativePathToNotAModule2.pkl new file mode 100644 index 000000000..fa394c0bc --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/relativePathToNotAModule2.pkl @@ -0,0 +1,8 @@ +local objectThatAmendsModule = (module) { + // add a local property to force this into a new object amends; + // empty object amends are optimized away. + local qux = 1 +} + +res = module.relativePathTo(objectThatAmendsModule) + diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/relativePathToNotAModule1.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/relativePathToNotAModule1.err new file mode 100644 index 000000000..cbb783caf --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/relativePathToNotAModule1.err @@ -0,0 +1,14 @@ +–– Pkl Error –– +Expected a module as the receiver, but got an object that amends a module. + +x | res = objectThatAmendsModule.relativePathTo(module) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +at relativePathToNotAModule1#res (file:///$snippetsDir/input/errors/relativePathToNotAModule1.pkl) + +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) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/relativePathToNotAModule2.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/relativePathToNotAModule2.err new file mode 100644 index 000000000..991785fd9 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/relativePathToNotAModule2.err @@ -0,0 +1,14 @@ +–– Pkl Error –– +Expected a module as argument, but got an object that amends a module. + +x | res = module.relativePathTo(objectThatAmendsModule) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +at relativePathToNotAModule2#res (file:///$snippetsDir/input/errors/relativePathToNotAModule2.pkl) + +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)